diff --git a/e2e-test/helpers/start-alfresco.sh b/e2e-test/helpers/start-alfresco.sh new file mode 100755 index 000000000..4aa6eda29 --- /dev/null +++ b/e2e-test/helpers/start-alfresco.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -eux + +# Start Alfresco and Solr. + +# The location for the docker-compose files. +DOCKER_RESOURCE_FOLDER=$1 +# The search docker image. +SEARCH_IMAGE=$2 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +export DOCKER_CLIENT_TIMEOUT=120 +export COMPOSE_HTTP_TIMEOUT=120 + +# Pull the image, to ensure we're not using a stale image from the build agent. +docker pull "$SEARCH_IMAGE" + +# Build the images and call docker-compose. +cd "$DOCKER_RESOURCE_FOLDER" +docker-compose up -d --build --force-recreate + +$SCRIPT_DIR/wait-service-to-start.sh diff --git a/e2e-test/helpers/wait-service-to-start.sh b/e2e-test/helpers/wait-service-to-start.sh new file mode 100755 index 000000000..e8ad3e332 --- /dev/null +++ b/e2e-test/helpers/wait-service-to-start.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e # exit if commands fails +set -x # trace what gets exe + +WAIT_INTERVAL=1 +COUNTER=0 +TIMEOUT=2000 +t0=`date +%s` + +ENDPOINT="${1:-http://localhost:8081/alfresco}" + +echo "Waiting for Service to start using endpoint: ${ENDPOINT}" + +until $(curl --output /dev/null --silent --head --fail ${ENDPOINT}) || [ "$COUNTER" -eq "$TIMEOUT" ]; do + printf '.' + sleep $WAIT_INTERVAL + COUNTER=$(($COUNTER+$WAIT_INTERVAL)) +done + +if (("$COUNTER" < "$TIMEOUT")) ; then + t1=`date +%s` + delta=$((($t1 - $t0)/60)) + echo "Service ${ENDPOINT} Started in $delta minutes" +else + echo "Service ${ENDPOINT} could not start in time." + echo "Waited $COUNTER seconds" + exit 1 +fi diff --git a/e2e-test/python-generator/README.md b/e2e-test/python-generator/README.md new file mode 100644 index 000000000..e61d02e94 --- /dev/null +++ b/e2e-test/python-generator/README.md @@ -0,0 +1,46 @@ +# About +A python script to generator docker-compose files suitable for use with the automated tests in +the [Search and Insight E2E tests](https://git.alfresco.com/search_discovery/insightengine/tree/master/e2e-test). + +# Installation +The script uses Python 3, which can be installed from [python.org](https://www.python.org/downloads/) or using +a package manager. You will also need the [yaml library](https://pypi.org/project/PyYAML/), which can be installed +using Pip if it is not already present: +``` +pip3 install pyyaml +``` + +# Using the script +The script provides some help with the `-h` option: + +``` +python3 BuildScripts/generator/generator.py -h +usage: generator.py [-h] [-a ALFRESCO]... +... +``` + +For use with ACS 6.2.x the legacy transformers can be included: +``` +python3 BuildScripts/generator/generator.py --alfresco=alfresco/alfresco-content-repository:6.2.0 --transformer=LegacyTransformers +``` + +For ACS 6.0.x and ACS 6.1.x no external transformer is needed: +``` +python3 BuildScripts/generator/generator.py --alfresco=alfresco/alfresco-content-repository:6.1.0 +``` + +For ACS 6.0.0.x and earlier ActiveMQ can be excluded: +``` +python3 BuildScripts/generator/generator.py --alfresco=alfresco/alfresco-content-repository:6.0.0.3 --excludeAMQ +``` + +For ACS 5.2.x the legacy LibreOffice transformer can be used: +``` +python3 BuildScripts/generator/generator.py --alfresco=quay.io/alfresco/alfresco-content-repository-52:5.2.5 --postgres=postgres:9.4 --excludeAMQ --transformer=LibreOffice +``` + +# Starting the containers +To start the containers you also need to build the images - for example: +``` +docker-compose up --build --force-recreate +``` diff --git a/e2e-test/python-generator/generator.py b/e2e-test/python-generator/generator.py new file mode 100644 index 000000000..99cf11d68 --- /dev/null +++ b/e2e-test/python-generator/generator.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import argparse +from string import Template +import yaml +import os +from distutils.dir_util import copy_tree + +LIBRE_OFFICE = 'LibreOffice' +LEGACY_TRANSFORMERS = 'LegacyTransformers' + +AMQ_OPTS = '-Dmessaging.broker.url="failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true"' +TRANSFORM_OPTS = ('-Dtransform.service.enabled=false -Dlocal.transform.service.enabled=true ' + '-DlocalTransform.libreoffice.url=http://libreoffice:8090/ -DlocalTransform.tika.url=http://tika:8090/ ' + '-DlocalTransform.misc.url=http://transform-misc:8090/ -Dlegacy.transform.service.enabled=true ' + '-Djodconverter.url=http://libreoffice:8090/ -Dtika.url=http://tika:8090/ ' + '-Dtransform.misc.url=http://transform-misc:8090/') +SHARE_TRANSFORM_OPTS = ('-DlocalTransform.pdfrenderer.url=http://alfresco-pdf-renderer:8090/ ' + '-Dalfresco-pdf-renderer.url=http://alfresco-pdf-renderer:8090/ ' + '-DlocalTransform.imagemagick.url=http://imagemagick:8090/ -Dimg.url=http://imagemagick:8090/') +SHARDING_OPTS = '-Dsolr.useDynamicShardRegistration=true' +JAVA_OPTS = ('-Ddb.driver=org.postgresql.Driver -Ddb.username=alfresco -Ddb.password=alfresco ' + '-Ddb.url=jdbc:postgresql://postgres:5432/alfresco -Dsolr.port=8983 ' + '-Dindex.subsystem.name=solr6 ' + '-Dalfresco.restApi.basicAuthScheme=true ') +MTLS_OPTS = ('-Dsolr.port.ssl=8983 -Dsolr.secureComms=https ') +HTTP_OPTS = ('-Dsolr.secureComms=none') +JAVA_TOOL_OPTIONS = ('-Dmetadata-keystore.password=mp6yc0UD9e ' + '-Dmetadata-keystore.aliases=metadata ' + '-Dmetadata-keystore.metadata.password=mp6yc0UD9e ' + '-Dmetadata-keystore.metadata.algorithm=AES') +MTLS_JAVA_TOOL_OPTIONS = ('-Dencryption.keystore.type=pkcs12 -Dencryption.cipherAlgorithm=AES/CBC/PKCS5Padding ' + '-Dencryption.keyAlgorithm=AES ' + '-Dssl-truststore.password=kT9X6oe68t -Dssl-keystore.password=kT9X6oe68t ' + '-Dssl-keystore.aliases=ssl-alfresco-ca,ssl-repo ' + '-Dssl-keystore.ssl-alfresco-ca.password=kT9X6oe68t ' + '-Dssl-keystore.ssl-repo.password=kT9X6oe68t ' + '-Dssl-truststore.aliases=alfresco-ca,ssl-repo-client ' + '-Dssl-truststore.alfresco-ca.password=kT9X6oe68t ' + '-Dssl-truststore.ssl-repo-client.password=kT9X6oe68t') + +def getJavaOpts(includeAMQ, includeTransform, includeShare, solrHost, solrBaseUrl, sharding, mtls): + + solrHost = '-Dsolr.host=' + solrHost + shardingOpts = (SHARDING_OPTS if sharding != None else '') + amqOpts = (AMQ_OPTS if includeAMQ else '') + transformOpts = (TRANSFORM_OPTS if includeTransform else '') + solrBaseUrlOpts = '-Dsolr.baseUrl=' + solrBaseUrl + shareTransformOpts = (SHARE_TRANSFORM_OPTS if includeShare and includeTransform else '') + mtlsOpts = (MTLS_OPTS if mtls else HTTP_OPTS) + + return ' '.join([JAVA_OPTS, amqOpts, transformOpts, shareTransformOpts, solrHost, solrBaseUrlOpts, shardingOpts, mtlsOpts]) + +def getJavaToolOptions(mtls): + + mtlsJavaToolOptions = (MTLS_JAVA_TOOL_OPTIONS if mtls else '') + + return ' '.join([JAVA_TOOL_OPTIONS, mtlsJavaToolOptions]) + +def deleteServices(dcYaml, *services): + for service in services: + if service in dcYaml['services'].keys(): + del(dcYaml['services'][service]) + +def getExtraEnvironmentVars(serviceName, replicationType): + """Return a dict of environment variables to add to the search container declaration in docker-compose.yml.""" + extraEnvironmentVars = {} + if replicationType == 'master': + extraEnvironmentVars['REPLICATION_TYPE'] = 'master' + elif replicationType == 'slave': + extraEnvironmentVars.update({'REPLICATION_TYPE': 'slave', + 'REPLICATION_MASTER_HOST': serviceName.replace('slave', 'master'), + 'REPLICATION_MASTER_PORT': '8983', + 'REPLICATION_POLL_INTERVAL': '00:00:10'}) + return extraEnvironmentVars + +def getSolrcoreConfig(sharding, shardId, shardCount, shardRange): + """Returns a list of properties to add to the end of the solrcore.properties file.""" + solrcoreConfig = [] + if sharding != None: + solrcoreConfig.append('solr.port.ssl=8983') + solrcoreConfig.append('shard.instance={}'.format(shardId)) + solrcoreConfig.append('alfresco.port=8080') + solrcoreConfig.append('alfresco.port.ssl=8443') + solrcoreConfig.append('alfresco.baseUrl=/alfresco') + if sharding not in ['DB_ID_RANGE', 'EXPLICIT_ID_FALLBACK_LRIS']: + solrcoreConfig.append('shard.count={}'.format(shardCount)) + if sharding == 'DB_ID_RANGE': + # The first shards each contain 800 nodes by default and the last continues the range to id 100000. + nodesPerShard = shardRange + rangeStart = shardId * nodesPerShard + rangeEnd = (rangeStart + nodesPerShard - 1 if shardId < shardCount - 1 else 100000) + solrcoreConfig.append('shard.range={}-{}'.format(rangeStart, rangeEnd)) + if sharding == 'DATE': + solrcoreConfig.append('shard.key=cm:created') + solrcoreConfig.append('shard.date.grouping={}'.format(12 // shardCount)) + if sharding in ['PROPERTY', 'EXPLICIT_ID', 'EXPLICIT_ID_FALLBACK_LRIS']: + solrcoreConfig.append('shard.key=shard:shardId') + print("SolrConfig for Shard: ", shardId, " : ", solrcoreConfig) + return solrcoreConfig + +def getSolrcoreReplacements(sharding, mtls): + """Returns a dict of replacements to make in the solrcore.properties file.""" + solrcoreReplacements = {} + if sharding != None: + solrcoreReplacements['shard.method=DB_ID'] = 'shard.method={}'.format(sharding) + if mtls: + solrcoreReplacements['alfresco.secureComms=none'] = 'alfresco.secureComms=https' + solrcoreReplacements['alfresco.encryption.ssl.keystore.location=.*'] = 'alfresco.encryption.ssl.keystore.location=\\\\\\/opt\\\\\\/alfresco-search-services\\\\\\/keystore\\\\\\/ssl-repo-client.keystore' + solrcoreReplacements['alfresco.encryption.ssl.keystore.type=.*'] = 'alfresco.encryption.ssl.keystore.type=JCEKS' + solrcoreReplacements['alfresco.encryption.ssl.truststore.location=.*'] = 'alfresco.encryption.ssl.truststore.location=\\\\\\/opt\\\\\\/alfresco-search-services\\\\\\/keystore\\\\\\/ssl-repo-client.truststore' + solrcoreReplacements['alfresco.encryption.ssl.truststore.type=.*'] = 'alfresco.encryption.ssl.truststore.type=JCEKS' + else: + solrcoreReplacements['alfresco.secureComms=https'] = 'alfresco.secureComms=none' + return solrcoreReplacements + +def addAlfrescoMtlsConfig(alfrescoArgsNode): + """Add a list of environment values in Docker Compose Alfresco Service for mTLS.""" + alfrescoArgsNode['TRUSTSTORE_TYPE'] = 'JCEKS' + alfrescoArgsNode['TRUSTSTORE_PASS'] = 'kT9X6oe68t' + alfrescoArgsNode['KEYSTORE_TYPE'] = 'JCEKS' + alfrescoArgsNode['KEYSTORE_PASS'] = 'kT9X6oe68t' + alfrescoArgsNode['SOLR_COMMS'] = 'https' + +def addAlfrescoVolumes(alfrescoNode): + """Add route to keystores folder""" + alfrescoNode['volumes'] = ['./keystores/alfresco:/usr/local/tomcat/alf_data/keystore'] + +def addSolrMtlsConfig(solrEnvNode): + """Add a list of environment values in Docker Compose SOLR Service for mTLS.""" + solrEnvNode['SOLR_SSL_TRUST_STORE'] = '/opt/alfresco-search-services/keystore/ssl-repo-client.truststore' + solrEnvNode['SOLR_SSL_TRUST_STORE_TYPE'] = 'JCEKS' + solrEnvNode['SOLR_SSL_KEY_STORE'] = '/opt/alfresco-search-services/keystore/ssl-repo-client.keystore' + solrEnvNode['SOLR_SSL_KEY_STORE_TYPE'] = 'JCEKS' + solrEnvNode['SOLR_SSL_NEED_CLIENT_AUTH'] = 'true' + +def addSolrOpts(solrEnvNode): + """Add a list of values to add in Docker Compose SOLR_OPTS property for mTLS.""" + solrOptions = ' '.join(['-Dsolr.ssl.checkPeerName=false', + '-Dsolr.allow.unsafe.resourceloading=true']) + solrEnvNode['SOLR_OPTS'] = solrOptions + +def addSolrJavaToolOptions(solrEnvNode): + """Add a list of values to add in Docker Compose JAVA_TOOL_OPTIONS property for mTLS.""" + solrOptions = ' '.join(['-Dsolr.jetty.truststore.password=kT9X6oe68t ', + '-Dsolr.jetty.keystore.password=kT9X6oe68t ', + '-Dssl-keystore.password=kT9X6oe68t', + '-Dssl-keystore.aliases=ssl-alfresco-ca,ssl-repo-client', + '-Dssl-keystore.ssl-alfresco-ca.password=kT9X6oe68t', + '-Dssl-keystore.ssl-repo-client.password=kT9X6oe68t', + '-Dssl-truststore.password=kT9X6oe68t', + '-Dssl-truststore.aliases=ssl-alfresco-ca,ssl-repo,ssl-repo-client', + '-Dssl-truststore.ssl-alfresco-ca.password=kT9X6oe68t', + '-Dssl-truststore.ssl-repo.password=kT9X6oe68t', + '-Dssl-truststore.ssl-repo-client.password=kT9X6oe68t']) + solrEnvNode['JAVA_TOOL_OPTIONS'] = solrOptions + +def addSolrVolumes(solrNode): + """Add route to keystores folder""" + solrNode['volumes'] = ['./keystores/solr:/opt/alfresco-search-services/keystore'] + +def makeSearchNode(outputDirectory, nodeName, externalPort, params, mtls, extraEnvironmentVars={}, solrcoreConfig=[], solrcoreReplacements={}): + # Create a dictionary for the template replacement. + allParams = dict(params) + allParams['SOLR_HOST'] = nodeName + allParams['ALFRESCO_PORT'] = 8443 if mtls else 8080 + allParams['EXTERNAL_PORT'] = externalPort + # Properties to add to solrcore.properties. + allParams['SOLRCORE_PROPERTIES'] = '\\n'.join(solrcoreConfig) + # Replacements to make in solrcore.properties (in an "abc/xyz" format suitable for sed). + allParams['SOLRCORE_REPLACEMENTS'] = ' '.join(map(lambda pair: '"{}/{}"'.format(*pair), solrcoreReplacements.items())) + + # mTLS settings + allParams['ALFRESCO_SECURE_COMMS'] = ('https' if mtls else 'none') + allParams['TRUSTSTORE_TYPE'] = 'JCEKS' + allParams['KEYSTORE_TYPE'] = 'JCEKS' + + # Create a Dockerfile with any extra configuration in. + with open(scriptDir + '/templates/search/Dockerfile.template') as f: + dockerfileTemplate = f.read() + dockerfileString = Template(dockerfileTemplate).substitute(allParams) + if not os.path.isdir('{}/{}'.format(outputDirectory, nodeName)): + os.mkdir('{}/{}'.format(outputDirectory, nodeName)) + with open('{}/{}/Dockerfile'.format(outputDirectory, nodeName), 'w') as f: + dockerfileTemplate = f.write(dockerfileString) + + # Load the search node template. + with open(scriptDir + '/templates/search-node.yml.template') as f: + searchNodeTemplate = f.read() + searchNodeString = Template(searchNodeTemplate).substitute(allParams) + # Read the result as yaml. + searchNodeYaml = yaml.safe_load(searchNodeString) + # Add any extra environment variables. + searchNodeYaml['environment'].update(extraEnvironmentVars) + + # Add mTLS configuration if required + if mtls: + addSolrMtlsConfig(searchNodeYaml['environment']) + addSolrOpts(searchNodeYaml['environment']) + addSolrJavaToolOptions(searchNodeYaml['environment']) + addSolrVolumes(searchNodeYaml) + + return searchNodeYaml + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Generate a docker-compose file for ACS.') + parser.add_argument('-a', '--alfresco', default='quay.io/alfresco/dev:acs-for-search', help='The Alfresco image') + parser.add_argument('-s', '--search', default='quay.io/alfresco/search-services:latest', help='The Search image') + parser.add_argument('-e', '--share', help='The Share image (or omit for no UI)') + parser.add_argument('-p', '--postgres', default='postgres:10.1', help='The Postgres image') + parser.add_argument('-q', '--excludeAMQ', action='store_true', help='Exclude ActiveMQ (i.e. pre-ACS 6.1)') + parser.add_argument('-t', '--transformer', choices=[LIBRE_OFFICE, LEGACY_TRANSFORMERS], help='Use external transformers. ' + + '"{}" for legacy LibreOffice (i.e. ACS 5.2.x). '.format(LIBRE_OFFICE) + + '"{}" for the transformers for use with ACS 6.2.x and later.'.format(LEGACY_TRANSFORMERS)) + parser.add_argument('-c', '--spellcheck', action='store_true', help='Spellcheck Enabled') + parser.add_argument('-ms', '--masterslave', action='store_true', help='Master Slave Enabled') + parser.add_argument('-sh', '--sharding', help='Sharding method (or omit for no sharding). Note that sharding is not supported on SearchServices 1.2.x or earlier.', + choices=['DB_ID', 'DB_ID_RANGE', 'ACL_ID', 'MOD_ACL_ID', 'DATE', 'PROPERTY', 'LRIS', 'EXPLICIT_ID', 'EXPLICIT_ID_FALLBACK_LRIS']) + parser.add_argument('-sc', '--shardCount', type=int, help='Total number of shards to create (default 2)') + parser.add_argument('-sr', '--shardRange', type=int, help='Total number of nodes per shard with DB_ID_RANGE sharding (default 800)') + parser.add_argument('-ct', '--disableCascadeTracking', action='store_true', help='Cascade Tracking Disabled') + parser.add_argument('-o', '--output', default='.', help='The path of the directory to output to') + parser.add_argument('-mtls', '--mtls', action='store_true', help='Use mTLS between SOLR and Alfresco Repository') + args = parser.parse_args() + + # If sharding is selected then the default number of shards is two. + if args.sharding != None and args.shardCount == None: + print('Using default shardCount of 2') + args.shardCount = 2 + elif args.sharding == None and args.shardCount != None: + print('ERROR: shardCount={} specified without sharding method'.format(args.shardCount)) + exit(1) + print('Arguments:', args) + + # If sharding is selected then the default number of nodes per shard is 800. + if args.sharding == "DB_ID_RANGE" and (args.shardRange == None or args.shardRange < 1): + print('Using default shardRange of 800') + args.shardRange = 800 + elif args.sharding != 'DB_ID_RANGE' and args.shardRange != None: + print('ERROR: shardRange={} is only supported for DB_ID_RANGE sharding.') + exit(1) + print('Arguments:', args) + + # Load the template and perform basic token substitution. + scriptDir = os.path.dirname(os.path.realpath(__file__)) + with open(scriptDir + '/templates/docker-compose.yml.template') as f: + template = f.read() + params = { + 'ALFRESCO_IMAGE': args.alfresco, + 'SHARE_IMAGE': args.share, + 'POSTGRES_IMAGE': args.postgres, + 'SEARCH_IMAGE': args.search, + 'SEARCH_ENABLE_SPELLCHECK': str(args.spellcheck).lower(), + 'DISABLE_CASCADE_TRACKING': str(args.disableCascadeTracking).lower() + } + dcString = Template(template).substitute(params) + + # Edit the resulting yaml. + dcYaml = yaml.safe_load(dcString) + + # Insert the search node(s). + shardList = range(args.shardCount) if args.sharding != None else [0] + replicationTypes = ['master', 'slave'] if args.masterslave else ['standalone'] + for shardId in shardList: + for replicationType in replicationTypes: + serviceName = 'search_{}_{}'.format(shardId, replicationType) + # Workaround for ShardInfoTest.getShardInfoWithAdminAuthority. + if shardId == 0 and replicationType == 'standalone': + serviceName = 'search' + externalPort = 8083 + 100 * shardId + (1 if replicationType == 'slave' else 0) + dcYaml['services'][serviceName] = makeSearchNode(args.output, serviceName, externalPort, params, args.mtls, + extraEnvironmentVars=getExtraEnvironmentVars(serviceName, replicationType), + solrcoreConfig=getSolrcoreConfig(args.sharding, shardId, args.shardCount, args.shardRange), + solrcoreReplacements=getSolrcoreReplacements(args.sharding, args.mtls)) + + # Point Alfresco at whichever Solr node came last in the list. + solrHost = serviceName + solrBaseUrl = '/solr-slave' if args.masterslave else '/solr' + + javaOpts = getJavaOpts(not args.excludeAMQ, args.transformer == LEGACY_TRANSFORMERS, args.share != None, solrHost, solrBaseUrl, args.sharding, args.mtls) + dcYaml['services']['alfresco']['environment']['JAVA_OPTS'] = javaOpts + javaToolOpts = getJavaToolOptions(args.mtls) + dcYaml['services']['alfresco']['environment']['JAVA_TOOL_OPTIONS'] = javaToolOpts + if args.mtls: + addAlfrescoMtlsConfig(dcYaml['services']['alfresco']['build']['args']) + addAlfrescoVolumes(dcYaml['services']['alfresco']) + + if not args.share: + deleteServices(dcYaml, 'share', 'alfresco-pdf-renderer', 'imagemagick') + if args.excludeAMQ: + deleteServices(dcYaml, 'activemq') + if args.transformer != LEGACY_TRANSFORMERS: + deleteServices(dcYaml, 'libreoffice', 'tika', 'transform-misc', 'shared-file-store', 'alfresco-pdf-renderer', 'imagemagick') + del(dcYaml['volumes']['shared-file-store-volume']) + if args.transformer == LIBRE_OFFICE: + dcYaml['services']['libreoffice'] = {'image': 'xcgd/libreoffice'} + + # Output the yaml. + with open(args.output + '/docker-compose.yml', 'w') as f: + f.write(yaml.safe_dump(dcYaml)) + + # Create an Alfresco Dockerfile with any extra configuration in. + with open(scriptDir + '/templates/alfresco/Dockerfile.template') as f: + dockerfileTemplate = f.read() + dockerfileString = Template(dockerfileTemplate).substitute(params) + if not os.path.isdir('{}/{}'.format(args.output, 'alfresco')): + os.mkdir('{}/{}'.format(args.output, 'alfresco')) + with open('{}/{}/Dockerfile'.format(args.output, 'alfresco'), 'w') as f: + dockerfileTemplate = f.write(dockerfileString) + + # Copy the keystores (when using mTLS) + if args.mtls: + copy_tree(scriptDir + '/keystores', args.output + '/keystores') diff --git a/e2e-test/python-generator/keystores/alfresco/keystore b/e2e-test/python-generator/keystores/alfresco/keystore new file mode 100644 index 000000000..439bc1f86 Binary files /dev/null and b/e2e-test/python-generator/keystores/alfresco/keystore differ diff --git a/e2e-test/python-generator/keystores/alfresco/ssl.keystore b/e2e-test/python-generator/keystores/alfresco/ssl.keystore new file mode 100644 index 000000000..c0cff56f8 Binary files /dev/null and b/e2e-test/python-generator/keystores/alfresco/ssl.keystore differ diff --git a/e2e-test/python-generator/keystores/alfresco/ssl.truststore b/e2e-test/python-generator/keystores/alfresco/ssl.truststore new file mode 100644 index 000000000..33da2c362 Binary files /dev/null and b/e2e-test/python-generator/keystores/alfresco/ssl.truststore differ diff --git a/e2e-test/python-generator/keystores/solr/ssl-repo-client.keystore b/e2e-test/python-generator/keystores/solr/ssl-repo-client.keystore new file mode 100644 index 000000000..f1474dd04 Binary files /dev/null and b/e2e-test/python-generator/keystores/solr/ssl-repo-client.keystore differ diff --git a/e2e-test/python-generator/keystores/solr/ssl-repo-client.truststore b/e2e-test/python-generator/keystores/solr/ssl-repo-client.truststore new file mode 100644 index 000000000..c09e6891a Binary files /dev/null and b/e2e-test/python-generator/keystores/solr/ssl-repo-client.truststore differ diff --git a/e2e-test/python-generator/templates/alfresco/Dockerfile.template b/e2e-test/python-generator/templates/alfresco/Dockerfile.template new file mode 100755 index 000000000..3438f963e --- /dev/null +++ b/e2e-test/python-generator/templates/alfresco/Dockerfile.template @@ -0,0 +1,49 @@ +FROM ${ALFRESCO_IMAGE} + +ENV TOMCAT_DIR=/usr/local/tomcat +ENV ALF_DATA_DIR=$${TOMCAT_DIR}/alf_data + +# COMMS +ARG SOLR_COMMS +ENV SOLR_COMMS $$SOLR_COMMS + +# SSL +ARG TRUSTSTORE_TYPE +ARG TRUSTSTORE_PASS +ARG KEYSTORE_TYPE +ARG KEYSTORE_PASS + +ENV TRUSTSTORE_TYPE=$$TRUSTSTORE_TYPE \ + TRUSTSTORE_PASS=$$TRUSTSTORE_PASS \ + KEYSTORE_TYPE=$$KEYSTORE_TYPE \ + KEYSTORE_PASS=$$KEYSTORE_PASS + +USER root + +# Default value in 'repository.properties' is 'dir.keystore=classpath:alfresco/keystore' +RUN if [ "$$SOLR_COMMS" == "https" ] ; then \ + echo -e "\n\ + dir.keystore=$${ALF_DATA_DIR}/keystore\n\ + alfresco.encryption.ssl.keystore.type=$${TRUSTSTORE_TYPE}\n\ + alfresco.encryption.ssl.truststore.type=$${KEYSTORE_TYPE}\n\ + " >> $${TOMCAT_DIR}/shared/classes/alfresco-global.properties; \ + fi + +# Enable SSL by adding the proper Connector to server.xml +RUN if [ "$$SOLR_COMMS" == "https" ] ; then \ + sed -i "s/\ +[[:space:]]\+<\/Engine>/\n\ + <\/Engine>\n\ + \n\ + <\/Connector>/g" $${TOMCAT_DIR}/conf/server.xml; \ + fi + +# Expose keystore folder +# Useless for 'none'/'http' communications with SOLR +VOLUME ["$$ALF_DATA_DIR/keystore"] diff --git a/e2e-test/python-generator/templates/docker-compose.yml.template b/e2e-test/python-generator/templates/docker-compose.yml.template new file mode 100644 index 000000000..b2c3f0145 --- /dev/null +++ b/e2e-test/python-generator/templates/docker-compose.yml.template @@ -0,0 +1,101 @@ +version: '3' +services: + alfresco: + build: + context: ./alfresco + args: + SOLR_COMMS: none + environment: + JAVA_OPTS : "This will be populated by the generator script" + JAVA_TOOL_OPTIONS : "This will be populated by the generator script" + ports: + - "7203:7203" #JMX connect via service:jmx:rmi:///jndi/rmi://localhost:7203/jmxrmi + - "5005:5005" #Java debugging + - "8081:8080" #Browser port for Alfresco + share: + image: ${SHARE_IMAGE} + environment: + - REPO_HOST=alfresco + - REPO_PORT=8080 + ports: + - 8082:8080 #Browser port for Share + postgres: + image: ${POSTGRES_IMAGE} + environment: + - POSTGRES_PASSWORD=alfresco + - POSTGRES_USER=alfresco + - POSTGRES_DB=alfresco + ports: + - 5432:5432 + activemq: + image: alfresco/alfresco-activemq:5.15.6 + ports: + - 8161:8161 # Web Console + - 5672:5672 # AMQP + - 61616:61616 # OpenWire + - 61613:61613 # STOMP + alfresco-pdf-renderer: + image: alfresco/alfresco-pdf-renderer:2.1.0-EA4 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + ACTIVEMQ_URL: "nio://activemq:61616" + FILE_STORE_URL: "http://shared-file-store:8099/alfresco/api/-default-/private/sfs/versions/1/file" + ports: + - 8090:8090 + links: + - activemq + imagemagick: + image: alfresco/alfresco-imagemagick:2.1.0-EA4 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + ACTIVEMQ_URL: "nio://activemq:61616" + FILE_STORE_URL: "http://shared-file-store:8099/alfresco/api/-default-/private/sfs/versions/1/file" + ports: + - 8091:8090 + links: + - activemq + libreoffice: + image: alfresco/alfresco-libreoffice:2.1.0-EA4 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + ACTIVEMQ_URL: "nio://activemq:61616" + FILE_STORE_URL: "http://shared-file-store:8099/alfresco/api/-default-/private/sfs/versions/1/file" + ports: + - 8092:8090 + links: + - activemq + tika: + image: alfresco/alfresco-tika:2.1.0-EA4 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + ACTIVEMQ_URL: "nio://activemq:61616" + FILE_STORE_URL: "http://shared-file-store:8099/alfresco/api/-default-/private/sfs/versions/1/file" + ports: + - 8093:8090 + links: + - activemq + transform-misc: + image: alfresco/alfresco-transform-misc:2.1.0-EA4 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + ACTIVEMQ_URL: "nio://activemq:61616" + FILE_STORE_URL: "http://shared-file-store:8099/alfresco/api/-default-/private/sfs/versions/1/file" + ports: + - 8094:8090 + links: + - activemq + shared-file-store: + image: alfresco/alfresco-shared-file-store:0.5.3 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + scheduler.content.age.millis: 86400000 + scheduler.cleanup.interval: 86400000 + ports: + - 8099:8099 + volumes: + - shared-file-store-volume:/tmp/Alfresco/sfs +volumes: + shared-file-store-volume: + driver_opts: + type: tmpfs + device: tmpfs diff --git a/e2e-test/python-generator/templates/search-node.yml.template b/e2e-test/python-generator/templates/search-node.yml.template new file mode 100644 index 000000000..502be27f7 --- /dev/null +++ b/e2e-test/python-generator/templates/search-node.yml.template @@ -0,0 +1,17 @@ +build: + context: ./${SOLR_HOST} +environment: + #Solr needs to know how to register itself with Alfresco + SOLR_ALFRESCO_HOST: "alfresco" + SOLR_ALFRESCO_PORT: "${ALFRESCO_PORT}" + #Alfresco needs to know how to call solr + SOLR_SOLR_HOST: "${SOLR_HOST}" + SOLR_SOLR_PORT: "8983" + #Create the default alfresco and archive cores + SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive" + #Enable Spellcheck by setting to true + ENABLE_SPELLCHECK: "${SEARCH_ENABLE_SPELLCHECK}" + #Disable Cascade Tracking + DISABLE_CASCADE_TRACKING: "${DISABLE_CASCADE_TRACKING}" +ports: + - ${EXTERNAL_PORT}:8983 #Browser port diff --git a/e2e-test/python-generator/templates/search/Dockerfile.template b/e2e-test/python-generator/templates/search/Dockerfile.template new file mode 100644 index 000000000..70d97b9a7 --- /dev/null +++ b/e2e-test/python-generator/templates/search/Dockerfile.template @@ -0,0 +1,26 @@ +FROM ${SEARCH_IMAGE} + +# Create search_config_setup.sh if it does not exist (e.g. on SearchServices 1.2.x or earlier). +USER root +RUN touch $${DIST_DIR}/solr/bin/search_config_setup.sh \ + && chown solr:solr $${DIST_DIR}/solr/bin/search_config_setup.sh +USER solr + +RUN replacementPairs=(${SOLRCORE_REPLACEMENTS}); \ + for replacementPair in $${replacementPairs[@]}; \ + do \ + sed -i '/^bash.*/i sed -i "'"s/$$replacementPair/g"'" $${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \ + $${DIST_DIR}/solr/bin/search_config_setup.sh; \ + done; \ + if [[ "${SOLRCORE_PROPERTIES}" != "" ]]; \ + then \ + sed -i '/^bash.*/i echo "\n${SOLRCORE_PROPERTIES}" >> $${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \ + $${DIST_DIR}/solr/bin/search_config_setup.sh; \ + fi + +USER root +RUN mkdir -p /opt/alfresco-search-services/keystore \ + && chown -R solr:solr /opt/alfresco-search-services/keystore +USER solr + +VOLUME ["/opt/alfresco-search-services/keystore"]