diff --git a/v3.4/Dockerfile-frontend-dev b/v3.4/Dockerfile-frontend-dev new file mode 100644 index 0000000..5f25e50 --- /dev/null +++ b/v3.4/Dockerfile-frontend-dev @@ -0,0 +1,7 @@ +FROM nubisware/conductor-frontend + +LABEL maintainer="Nubisware " + +# Copy NGINX default configuration +COPY ./config/nginx/conf.d/default-dev.conf /etc/nginx/conf.d/default.conf + diff --git a/v3.4/Dockerfile-frontend-pre b/v3.4/Dockerfile-frontend-pre new file mode 100644 index 0000000..05309ea --- /dev/null +++ b/v3.4/Dockerfile-frontend-pre @@ -0,0 +1,7 @@ +FROM nubisware/conductor-frontend + +LABEL maintainer="Nubisware " + +# Copy NGINX default configuration +COPY ./config/nginx/conf.d/default-pre.conf /etc/nginx/conf.d/default.conf + diff --git a/v3.4/Dockerfile-server-dev b/v3.4/Dockerfile-server-dev new file mode 100644 index 0000000..57a22fa --- /dev/null +++ b/v3.4/Dockerfile-server-dev @@ -0,0 +1,7 @@ +FROM nubisware/conductor-server3 + +LABEL maintainer="Nubisware " + +# Copy the compiled output to new image +COPY ./config/config-pg-es7.properties /app/config/config.properties + diff --git a/v3.4/Dockerfile-server-pre b/v3.4/Dockerfile-server-pre new file mode 100644 index 0000000..57a22fa --- /dev/null +++ b/v3.4/Dockerfile-server-pre @@ -0,0 +1,7 @@ +FROM nubisware/conductor-server3 + +LABEL maintainer="Nubisware " + +# Copy the compiled output to new image +COPY ./config/config-pg-es7.properties /app/config/config.properties + diff --git a/v3.4/build-dev-images.sh b/v3.4/build-dev-images.sh new file mode 100755 index 0000000..62f56ef --- /dev/null +++ b/v3.4/build-dev-images.sh @@ -0,0 +1,5 @@ +#docker build -t nubisware/conductor-server3:dev -f Dockerfile-server-dev . +#docker push nubisware/conductor-server3:dev + +docker build -t nubisware/conductor-frontend:dev -f Dockerfile-frontend-dev . +docker push nubisware/conductor-frontend:dev diff --git a/v3.4/build-pre-images.sh b/v3.4/build-pre-images.sh new file mode 100755 index 0000000..9a33bd2 --- /dev/null +++ b/v3.4/build-pre-images.sh @@ -0,0 +1,5 @@ +docker build -t nubisware/conductor-server3:pre -f Dockerfile-server-pre . +docker push nubisware/conductor-server3:pre + +docker build -t nubisware/conductor-frontend:pre -f Dockerfile-frontend-pre . +docker push nubisware/conductor-frontend:pre diff --git a/v3.4/dev/config/base-workers.cfg b/v3.4/config/base-workers-dev.cfg similarity index 100% rename from v3.4/dev/config/base-workers.cfg rename to v3.4/config/base-workers-dev.cfg diff --git a/v3.4/config/base-workers.cfg b/v3.4/config/base-workers.cfg new file mode 100644 index 0000000..00c11e2 --- /dev/null +++ b/v3.4/config/base-workers.cfg @@ -0,0 +1,11 @@ +[common] +loglevel = info +threads = 1 +pollrate = 1 + +[pymail] +server = smtp-relay.d4science.org +user = conductor_dev +password = +protocol = starttls +port = 587 diff --git a/v3.4/dev/config/config-pg-es7.properties b/v3.4/config/config-pg-es7.properties similarity index 100% rename from v3.4/dev/config/config-pg-es7.properties rename to v3.4/config/config-pg-es7.properties diff --git a/v3.4/dev/config/config-simple.properties b/v3.4/config/config-simple.properties similarity index 100% rename from v3.4/dev/config/config-simple.properties rename to v3.4/config/config-simple.properties diff --git a/v3.4/dev/config/nginx/conf.d/default.conf b/v3.4/config/nginx/conf.d/default-dev.conf similarity index 100% rename from v3.4/dev/config/nginx/conf.d/default.conf rename to v3.4/config/nginx/conf.d/default-dev.conf diff --git a/v3.4/config/nginx/conf.d/default-pre.conf b/v3.4/config/nginx/conf.d/default-pre.conf new file mode 100644 index 0000000..6644379 --- /dev/null +++ b/v3.4/config/nginx/conf.d/default-pre.conf @@ -0,0 +1,87 @@ +upstream conductor_server { + ip_hash; + server conductor-server:8080; +} + +map $http_authorization $source_auth { + default ""; +} + +js_var $auth_token; +js_var $pep_credentials; + +server { + listen 80; + server_name conductor; + + location / { + # This would be the directory where your React app's static files are stored at + root /usr/share/nginx/html; + try_files $uri /index.html; + } + + location /health { + proxy_set_header Host $host; + proxy_pass http://conductor_server; + } + + location /actuator/prometheus { + proxy_set_header Host $host; + proxy_pass http://conductor_server; + } + + location /api/ { + js_content pep.enforce; + } + + location @backend { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-NginX-Proxy true; + proxy_cache_bypass $http_upgrade; + proxy_redirect off; + proxy_pass http://conductor_server; + } + + location /jwt_verify_request { + internal; + proxy_method POST; + proxy_http_version 1.1; + proxy_set_header Authorization $pep_credentials; + proxy_set_header Content-Type "application/x-www-form-urlencoded"; + proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect"; + + proxy_ignore_headers Cache-Control Expires Set-Cookie; + gunzip on; + + proxy_cache token_responses; # Enable caching + proxy_cache_key $source_auth; # Cache for each source authentication + proxy_cache_lock on; # Duplicate tokens must wait + proxy_cache_valid 200 10s; # How long to use each response + } + + location /jwt_request { + internal; + proxy_method POST; + proxy_http_version 1.1; + proxy_set_header Authorization $pep_credentials; + proxy_set_header Content-Type "application/x-www-form-urlencoded"; + proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token"; + gunzip on; + } + + location /permission_request { + internal; + proxy_method POST; + proxy_http_version 1.1; + proxy_set_header Content-Type "application/x-www-form-urlencoded"; + proxy_set_header Authorization "Bearer $auth_token"; + proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token"; + gunzip on; + } + +} + + + diff --git a/v3.4/dev/config/nginx/config.js b/v3.4/dev/config/nginx/config.js deleted file mode 100644 index d603ec6..0000000 --- a/v3.4/dev/config/nginx/config.js +++ /dev/null @@ -1,139 +0,0 @@ -export default { config }; - -var config = { - "hosts" : [ - { - "hosts": ["conductor", "conductor.dev.d4science.org", "conductor.pre.d4science.org", "conductor.d4science.org"], - "audience" : "conductor-server", - "allow-basic-auth" : true, - "paths" : [ - { - "name" : "metadata", - "path" : "^/api/metadata/(taskdefs|workflow)/?.*$", - "methods" : [ - { - "method" : "GET", - "scopes" : ["get","list"] - } - ] - }, - { - "name" : "metadata.taskdefs", - "path" : "^/api/metadata/taskdefs/?.*$", - "methods" : [ - { - "method" : "POST", - "scopes" : ["create"] - }, - { - "method" : "DELETE", - "scopes" : ["delete"], - }, - { - "method" : "PUT", - "scopes" : ["update"], - } - ] - }, - { - "name" : "metadata.workflow", - "path" : "^/api/metadata/workflow/?.*$", - "methods" : [ - { - "method" : "POST", - "scopes" : ["create"] - }, - { - "method" : "DELETE", - "scopes" : ["delete"], - }, - { - "method" : "PUT", - "scopes" : ["update"], - } - ] - }, - { - "name" : "workflow", - "path" : "^/api/workflow/?.*$", - "methods" : [ - { - "method" : "GET", - "scopes" : ["get"], - }, - { - "method" : "POST", - "scopes" : ["start"], - }, - { - "method" : "DELETE", - "scopes" : ["terminate"], - } - ] - }, - { - "name" : "event", - "path" : "^/api/event/?.*$", - "methods" : [ - { - "method" : "GET", - "scopes" : ["get"], - }, - { - "method" : "POST", - "scopes" : ["create"], - }, - { - "method" : "DELETE", - "scopes" : ["delete"], - }, - { - "method" : "PUT", - "scopes" : ["update"], - } - ] - }, - { - "name" : "task", - "path" : "^/api/tasks/poll/.+$", - "methods" : [ - { - "method" : "GET", - "scopes" : ["poll"], - } - ] - }, - { - "name" : "queue", - "path" : "^/api/tasks/queue/.+$", - "methods" : [ - { - "method" : "GET", - "scopes" : ["get"], - } - ] - }, - { - "name" : "task", - "path" : "^/api/tasks[/]?$", - "methods" : [ - { - "method" : "POST", - "scopes" : ["update"], - } - ] - }, - { - "name" : "log", - "path" : "^/api/tasks/.+/log$", - "methods" : [ - { - "method" : "GET", - "scopes" : ["get"], - } - ] - } - ] - } - ] -} diff --git a/v3.4/dev/config/nginx/nginx.conf b/v3.4/dev/config/nginx/nginx.conf deleted file mode 100644 index 5796bf6..0000000 --- a/v3.4/dev/config/nginx/nginx.conf +++ /dev/null @@ -1,44 +0,0 @@ -# Added to load njs module -load_module modules/ngx_http_js_module.so; - -user nginx; -worker_processes auto; - -error_log /var/log/nginx/error.log notice; -pid /var/run/nginx.pid; - - -events { - worker_connections 1024; -} - - -http { - - # added to import pep script - js_import pep.js; - - # added to bind enforce function - js_set $authorization pep.enforce; - - # added to create cache for tokens and auth calls - proxy_cache_path /var/cache/nginx/pep keys_zone=token_responses:1m max_size=2m; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - #tcp_nopush on; - - keepalive_timeout 65; - - #gzip on; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/v3.4/dev/config/nginx/pep.js b/v3.4/dev/config/nginx/pep.js deleted file mode 100644 index d630a00..0000000 --- a/v3.4/dev/config/nginx/pep.js +++ /dev/null @@ -1,333 +0,0 @@ -export default { enforce }; - -import defaultExport from './config.js'; - -function log(c, s){ - c.request.error(s) -} - -var _debug = true -function debug(c, s){ - if(_debug === true){ - log(c, s) - } -} - -function enforce(r) { - - var context = { - request: r , - config : defaultExport["config"], - backend : (defaultExport.backend ? defaultExport.backend : "@backend") - } - - log(context, "Inside NJS enforce for " + r.method + " @ " + r.headersIn.host + "/" + r.uri) - - context = computeProtection(context) - - wkf.run(wkf.build(context), context) -} - -// ######## WORKFLOW FUNCTIONS ############### -var wkf = { - - build : (context)=>{ - var actions = [ - "export_pep_credentials", - "parse_authentication", - "check_authentication", - "export_authn_token", - "pip", - "pdp", - "export_backend_headers", - "pass" - ] - return actions - }, - - run : (actions, context) => { - context.request.error("Starting workflow with " + njs.dump(actions)) - var w = actions.reduce( - (acc, f) => { return acc.then(typeof(f) === "function" ? f : wkf[f]) }, - Promise.resolve().then(()=>context) - ) - w.catch(e => { context.request.error(njs.dump(e)); context.request.return(401)} ) - }, - - export_pep_credentials : exportPepCredentials, - export_authn_token : exportAuthToken, - export_backend_headers : exportBackendHeaders, - parse_authentication : parseAuthentication, - check_authentication : checkAuthentication, - verify_token : verifyToken, - request_token : requestToken, - pip : pipExecutor, - pdp : pdpExecutor, - pass : pass, - - //PIP utilities - "get-path-component" : (c, i) => c.request.uri.split("/")[i], - "get-token-field" : getTokenField, - "get-contexts" : (c) => { - var ra = c.authn.verified_token["resource_access"] - if(ra){ - var out = []; - for(var k in ra){ - if(ra[k].roles && ra[k].roles.length !== 0) out.push(k) - } - } - return out; - } -} - -function getTokenField(context, f){ - return context.authn.verified_token[f] -} - -function exportVariable(context, name, value){ - context.request.variables[name] = value - return context -} - -function exportBackendHeaders(context){ - return context -} - -function exportPepCredentials(context){ - log(context, njs.dump(process.env)) - if(process.env["pep-credentials"]){ - return exportVariable(context, "pep_credentials", "Basic " + process.env["pep-credentials"]) - }else if(context.config["pep-credentials"]){ - return exportVariable(context, "pep_credentials", "Basic " + context.config["pep-credentials"]) - }else{ - throw new Error("Need PEP credentials") - } -} - -function exportAuthToken(context){ - return exportVariable(context, "auth_token", context.authn.token) -} - -function checkAuthentication(context){ - return context.authn.type === "bearer" ? wkf.verify_token(context) : wkf.request_token(context) -} - -function parseAuthentication(context){ - context.request.log("Inside parseAuthentication") - var incomingauth = context.request.headersIn["Authorization"] - - if(!incomingauth) throw new Error("Authentication required"); - - var arr = incomingauth.trim().replace(/\s\s+/g, " ").split(" ") - if(arr.length != 2) throw new Error("Unknown authentication scheme"); - - var type = arr[0].toLowerCase() - if(type === "basic" && context.authz.host && context.authz.host["allow-basic-auth"]){ - var unamepass = Buffer.from(arr[1], 'base64').toString().split(":") - if(unamepass.length != 2) return null; - context.authn = { type : type, raw : arr[1], user : unamepass[0], password : unamepass[1]} - return context - }else if(type === "bearer"){ - context.authn = { type : type, raw : arr[1], token : arr[1]} - return context - } - throw new Error("Unknown authentication scheme"); -} - -function verifyToken(context){ - log(context, "Inside verifyToken") - debug(context, "Token is " + context.authn.token) - var options = { - "body" : "token=" + context.authn.token + "&token_type_hint=access_token" - } - return context.request.subrequest("/jwt_verify_request", options) - .then(reply=>{ - if (reply.status === 200) { - var response = JSON.parse(reply.responseBody); - if (response.active === true) { - return response - } else { - throw new Error("Unauthorized: " + reply.responseBody) - } - } else { - throw new Error("Unauthorized: " + reply.responseBody) - } - }).then(verified_token => { - context.authn.verified_token = - JSON.parse(Buffer.from(context.authn.token.split('.')[1], 'base64url').toString()) - return context - }) -} - -function requestToken(context){ - log(context, "Inside requestToken") - var options = { - "body" : "grant_type=client_credentials&client_id="+context.authn.user+"&client_secret="+context.authn.password - } - return context.request.subrequest("/jwt_request", options) - .then(reply=>{ - if (reply.status === 200) { - var response = JSON.parse(reply.responseBody); - context.authn.token = response.access_token - context.authn.verified_token = - JSON.parse(Buffer.from(context.authn.token.split('.')[1], 'base64url').toString()) - return context - } else if (reply.status === 400 || reply.status === 401){ - var options = { - "body" : "grant_type=password&username="+context.authn.user+"&password="+context.authn.password - } - return context.request.subrequest("/jwt_request", options) - .then( reply=>{ - if (reply.status === 200) { - var response = JSON.parse(reply.responseBody); - context.authn.token = response.access_token - context.authn.verified_token = - JSON.parse(Buffer.from(context.authn.token.split('.')[1], 'base64url').toString()) - return context - } else{ - throw new Error("Unauthorized " + reply.status) - } - }) - } else { - throw new Error("Unauthorized " + reply.status) - } - }) -} - -function pipExecutor(context){ - log(context, "Inside extra claims PIP") - context.authz.pip.forEach(extra =>{ - //call extra claim pip function - try{ - var operator = extra.operator - var result = wkf[operator](context, extra.args) - //ensure array and add to extra_claims - if(!(result instanceof Array)) result = [result] - if(!context.extra_claims) context.extra_claims = {}; - context.extra_claims[extra.claim] = result - } catch (error){ - log(context, "Skipping invalid extra claim " + njs.dump(error)) - } - }) - log(context, "Extra claims are " + njs.dump(context.extra_claims)) - return context -} - -function pdpExecutor(context){ - log(context, "Inside PDP") - return context.authz.pdp(context) -} - -function umaCall(context){ - log(context, "Inside UMA call") - var options = { "body" : computePermissionRequestBody(context) }; - return context.request.subrequest("/permission_request", options) - .then(reply =>{ - if(reply.status === 200){ - debug(context, "UMA call reply is " + reply.status) - return context - }else{ - throw new Error("Response for authorization request is not ok " + reply.status + " " + njs.dump(reply.responseBody)) - } - }) -} - -function pass(context){ - log(context, "Inside pass"); - if(typeof(context.backend) === "string") context.request.internalRedirect(context.backend); - else if (typeof(context.backend) === "function") context.request.internalRedirect(context.backend(context)) - return context; -} - -// ######## AUTHORIZATION PART ############### -function computePermissionRequestBody(context){ - - if(!context.authz.host || !context.authz.path ){ - throw new Error("Enforcemnt mode is always enforcing. Host or path not found...") - } - - var audience = computeAudience(context) - var grant = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" - var mode = "response_mode=decision" - var permissions = computePermissions(context) - var extra = "" - if(context.extra_claims){ - extra = - "claim_token_format=urn:ietf:params:oauth:token-type:jwt&claim_token=" + - JSON.stringify(context.extra_claims).toString("base64url") - } - var body = audience + "&" + grant + "&" + permissions + "&" + mode + "&" + extra - context.request.error("Computed permission request body is " + body) - return body -} - -function computeAudience(context){ - var aud = context.request.headersIn.host - if(context.authz.host){ - aud = context.authz.host.audience||context.authz.host.host - } - return "audience=" + aud -} - -function computePermissions(context){ - var resource = context.request.uri - if(context.authz.path){ - resource = context.authz.path.name||context.authz.path.path - } - var scopes = [] - if(context.authz.method && context.authz.method.scopes){ - scopes = context.authz.method.scopes - } - if(scopes.length > 0){ - return scopes.map(s=>"permission=" + resource + "#" + s).join("&") - } - return "permission=" + resource -} - -function getPath(hostconfig, incomingpath, incomingmethod){ - var paths = hostconfig.paths || [] - var matchingpaths = paths - .filter(p => {return incomingpath.match(p.path) != null}) - .reduce((acc, p) => { - if (!p.methods || p.methods.length === 0) acc.weak.push({ path: p}); - else{ - var matchingmethods = p.methods.filter(m=>m.method.toUpperCase() === incomingmethod) - if(matchingmethods.length > 0) acc.strong.push({ method : matchingmethods[0], path: p}); - } - return acc; - }, { strong: [], weak: []}) - return matchingpaths.strong.concat(matchingpaths.weak)[0] -} - -function getHost(config, host){ - var matching = config.hosts.filter(h=>{ - return h.host === host || h.hosts.indexOf(host) !== -1 - }) - - return matching.length > 0 ? matching[0] : null -} - -function computeProtection(context){ - debug(context, "Getting by host " + context.request.headersIn.host) - context.authz = {} - context.authz.host = getHost(context.config, context.request.headersIn.host) - if(context.authz.host !== null){ - log(context, "Host found:" + context.authz.host) - context.authz.pip = context.authz.host.pip ? context.authz.host.pip : []; - context.authz.pdp = context.authz.host.pdp ? context.authz.host.pdp : umaCall; - var pathandmethod = getPath(context.authz.host, context.request.uri, context.request.method); - if(pathandmethod){ - context.authz.path = pathandmethod.path; - context.authz.pip = context.authz.path.pip ? context.authz.pip.concat(context.authz.path.pip) : context.authz.pip; - context.authz.pdp = context.authz.path.pdp ? context.authz.path.pdp : context.authz.pdp; - context.authz.method = pathandmethod.method; - if(context.authz.method){ - context.authz.pip = context.authz.method.pip ? context.authz.pip.concat(context.authz.method.pip) : context.authz.pip; - context.authz.pdp = context.authz.method.pdp ? context.authz.method.pdp : context.authz.pdp; - } - } - } - debug(context, "Leaving protection computation: ") - return context -} - diff --git a/v3.4/dev/stack.yaml b/v3.4/stack-dev.yaml similarity index 68% rename from v3.4/dev/stack.yaml rename to v3.4/stack-dev.yaml index da9f511..fc18ad7 100644 --- a/v3.4/dev/stack.yaml +++ b/v3.4/stack-dev.yaml @@ -7,7 +7,7 @@ services: - POSTGRES_USER=conductor - POSTGRES_PASSWORD=conductor volumes: - - pg_db_data:/var/lib/postgresql + - pg_db_data:/var/lib/postgresql/data networks: - conductor-network healthcheck: @@ -56,7 +56,7 @@ services: conductor-server: environment: - CONFIG_PROP=config.properties - image: "nubisware/conductor-server3:latest" + image: "nubisware/conductor-server3:dev" networks: - conductor-network deploy: @@ -68,27 +68,14 @@ services: window: 120s placement: constraints: [node.role == worker] - configs: - - source: conductor-server-config - target: /app/config/config.properties - logging: driver: "journald" pep: - image: "nubisware/conductor-frontend" + image: "nubisware/conductor-frontend:dev" networks: - conductor-network - haproxy-public - configs: - - source: nginx-config - target: /etc/nginx/nginx.conf - - source: nginx-default-config - target: /etc/nginx/conf.d/default.conf - - source: nginx-pep - target: /etc/nginx/pep.js - - source: nginx-pep-config - target: /etc/nginx/config.js deploy: mode: replicated endpoint_mode: dnsrr @@ -101,16 +88,33 @@ services: constraints: [node.role == worker] environment: - pep-credentials: ${pep_credentials} - - base: + pep_credentials: ${pep_credentials} + + workers: environment: CONDUCTOR_SERVER: http://conductor-server:8080/api/ CONDUCTOR_HEALTH: http://conductor-server:8080/health - configs: - - source: base-worker-config - target: /app/config.cfg - image: 'nubisware/nubisware-conductor-worker-py-base' + worker_plugins: "Shell Eval Mail HttpBridge" + smtp_pass: ${smtp_pass} + image: 'nubisware/nubisware-conductor-worker-py-d4s' + networks: + - conductor-network + deploy: + mode: replicated + replicas: 2 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + + pyrestworkers: + environment: + CONDUCTOR_SERVER: http://conductor-server:8080/api/ + CONDUCTOR_HEALTH: http://conductor-server:8080/health + worker_plugins: Http + image: 'nubisware/nubisware-conductor-worker-py-d4s' networks: - conductor-network deploy: @@ -120,8 +124,6 @@ services: condition: any delay: 5s window: 120s - placement: - constraints: [node.role == worker] logging: driver: "journald" @@ -137,19 +139,3 @@ volumes: type: nfs4 o: "nfsvers=4,addr=146.48.123.250,rw" device: ":/nfs/conductor_pg_dev" - - -configs: - conductor-server-config: - file: ./config/config-pg-es7.properties - nginx-config: - file: ./config/nginx/nginx.conf - nginx-default-config: - file: ./config/nginx/conf.d/default.conf - nginx-pep: - file: ./config/nginx/pep.js - nginx-pep-config: - file: ./config/nginx/config.js - base-worker-config: - file: ./config/base-workers.cfg - diff --git a/v3.4/stack-local.yaml b/v3.4/stack-local.yaml new file mode 100644 index 0000000..96538e6 --- /dev/null +++ b/v3.4/stack-local.yaml @@ -0,0 +1,120 @@ +version: '3.6' + +services: + postgres: + image: postgres + environment: + - POSTGRES_USER=conductor + - POSTGRES_PASSWORD=conductor + networks: + - conductor-network + healthcheck: + test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5432' + interval: 5s + timeout: 5s + retries: 12 + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + + es: + image: elasticsearch:7.6.2 + environment: + - "ES_JAVA_OPTS=-Xms512m -Xmx1024m" + - transport.host=0.0.0.0 + - discovery.type=single-node + - xpack.security.enabled=false + networks: + - conductor-network + healthcheck: + test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/9300' + interval: 5s + timeout: 5s + retries: 12 + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + + conductor-server: + environment: + - CONFIG_PROP=config.properties + image: "nubisware/conductor-server3:dev" + networks: + - conductor-network + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + + pep: + image: "nubisware/conductor-frontend:dev" + networks: + - conductor-network + ports: + - "80:80" + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + environment: + pep_credentials: ${pep_credentials} + + workers: + environment: + CONDUCTOR_SERVER: http://conductor-server:8080/api/ + CONDUCTOR_HEALTH: http://conductor-server:8080/health + worker_plugins: "Shell Eval Mail HttpBridge" + smtp_pass: ${smtp_pass} + image: 'nubisware/nubisware-conductor-worker-py-d4s' + networks: + - conductor-network + deploy: + mode: replicated + replicas: 2 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + + pyrestworkers: + environment: + CONDUCTOR_SERVER: http://conductor-server:8080/api/ + CONDUCTOR_HEALTH: http://conductor-server:8080/health + worker_plugins: Http + image: 'nubisware/nubisware-conductor-worker-py-d4s' + networks: + - conductor-network + deploy: + mode: replicated + replicas: 2 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + +networks: + conductor-network: diff --git a/v3.4/stack-pre.yaml b/v3.4/stack-pre.yaml new file mode 100644 index 0000000..709b9b9 --- /dev/null +++ b/v3.4/stack-pre.yaml @@ -0,0 +1,141 @@ +version: '3.6' + +services: + postgres: + image: postgres + environment: + - POSTGRES_USER=conductor + - POSTGRES_PASSWORD=conductor + volumes: + - pg_db_data:/var/lib/postgresql/data + networks: + - conductor-network + healthcheck: + test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5432' + interval: 5s + timeout: 5s + retries: 12 + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + placement: + constraints: [node.role == worker] + logging: + driver: "journald" + + es: + image: elasticsearch:7.6.2 + environment: + - "ES_JAVA_OPTS=-Xms512m -Xmx1024m" + - transport.host=0.0.0.0 + - discovery.type=single-node + - xpack.security.enabled=false + networks: + - conductor-network + healthcheck: + test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/9300' + interval: 5s + timeout: 5s + retries: 12 + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + placement: + constraints: [node.role == worker] + logging: + driver: "journald" + + conductor-server: + environment: + - CONFIG_PROP=config.properties + image: "nubisware/conductor-server3:pre" + networks: + - conductor-network + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + placement: + constraints: [node.role == worker] + logging: + driver: "journald" + + pep: + image: "nubisware/conductor-frontend:pre" + networks: + - conductor-network + - haproxy-public + deploy: + mode: replicated + endpoint_mode: dnsrr + replicas: 1 + restart_policy: + condition: any + delay: 5s + window: 120s + placement: + constraints: [node.role == worker] + + environment: + pep_credentials: ${pep_credentials} + + workers: + environment: + CONDUCTOR_SERVER: http://conductor-server:8080/api/ + CONDUCTOR_HEALTH: http://conductor-server:8080/health + worker_plugins: "Shell Eval Mail HttpBridge" + smtp_pass: ${smtp_pass} + image: 'nubisware/nubisware-conductor-worker-py-d4s' + networks: + - conductor-network + deploy: + mode: replicated + replicas: 2 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + + pyrestworkers: + environment: + CONDUCTOR_SERVER: http://conductor-server:8080/api/ + CONDUCTOR_HEALTH: http://conductor-server:8080/health + worker_plugins: Http + image: 'nubisware/nubisware-conductor-worker-py-d4s' + networks: + - conductor-network + deploy: + mode: replicated + replicas: 2 + restart_policy: + condition: any + delay: 5s + window: 120s + logging: + driver: "journald" + +networks: + conductor-network: + haproxy-public: + external: true + +volumes: + pg_db_data: + driver: local + driver_opts: + type: nfs4 + o: "nfsvers=4,addr=146.48.123.250,rw" + device: ":/nfs/conductor_pg_pre"