forked from gCubeSystem/conductor-setup
added versions 3.136,3.13.8 and 3.15.0
This commit is contained in:
parent
e4585299d4
commit
112395d292
|
@ -0,0 +1,21 @@
|
|||
FROM nginx:alpine
|
||||
|
||||
LABEL maintainer="Nubisware <info@nubisware.com>"
|
||||
|
||||
# Bake common configurations for Conductor PEP
|
||||
COPY config/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY config/nginx/pep.js /etc/nginx/pep.js
|
||||
COPY config/nginx/config.js /etc/nginx/config.js
|
||||
|
||||
# Ensure that cache is invalidated
|
||||
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
|
||||
|
||||
# Copy compiled UI assets to nginx www directory
|
||||
WORKDIR /usr/share/nginx/html
|
||||
RUN rm -rf ./*
|
||||
COPY build/ .
|
||||
|
||||
# Copy NGINX default configuration
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
FROM nubisware/conductor-frontend:common
|
||||
|
||||
LABEL maintainer="Nubisware <info@nubisware.com>"
|
||||
|
||||
# Ensure that cache is invalidated
|
||||
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
|
||||
|
||||
# Copy compiled UI assets to nginx www directory
|
||||
WORKDIR /usr/share/nginx/html
|
||||
RUN rm -rf ./*
|
||||
COPY build/ .
|
||||
|
||||
# Copy NGINX default configuration
|
||||
COPY ./config.dev/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
FROM nubisware/conductor-frontend:common
|
||||
|
||||
LABEL maintainer="Nubisware <info@nubisware.com>"
|
||||
|
||||
# Ensure that cache is invalidated
|
||||
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
|
||||
|
||||
# Copy compiled UI assets to nginx www directory
|
||||
WORKDIR /usr/share/nginx/html
|
||||
RUN rm -rf ./*
|
||||
COPY build/ .
|
||||
|
||||
# Copy NGINX default configuration
|
||||
COPY ./config.pre/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
FROM nubisware/conductor-frontend:common
|
||||
|
||||
LABEL maintainer="Nubisware <info@nubisware.com>"
|
||||
|
||||
# Ensure that cache is invalidated
|
||||
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
|
||||
|
||||
# Copy compiled UI assets to nginx www directory
|
||||
WORKDIR /usr/share/nginx/html
|
||||
RUN rm -rf ./*
|
||||
COPY build/ .
|
||||
|
||||
# Copy NGINX default configuration
|
||||
COPY ./config.prod/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
|
||||
# ===========================================================================================================
|
||||
# 0. Builder stage
|
||||
# ===========================================================================================================
|
||||
FROM eclipse-temurin:11-jdk-focal AS builder
|
||||
|
||||
LABEL maintainer="Nubisware SRL"
|
||||
|
||||
# Copy the project directly onto the image
|
||||
COPY ./conductor-community /conductor
|
||||
COPY build.gradle /conductor/community-server/
|
||||
WORKDIR /conductor
|
||||
|
||||
# Build the server on run
|
||||
RUN ./gradlew generateLock updateLock saveLock
|
||||
RUN ./gradlew build -x test --stacktrace
|
||||
|
||||
# ===========================================================================================================
|
||||
# 1. Bin stage
|
||||
# ===========================================================================================================
|
||||
FROM eclipse-temurin:11-jre-focal
|
||||
|
||||
LABEL maintainer="Nubisware SRL"
|
||||
|
||||
# Make app folders
|
||||
RUN mkdir -p /app/config /app/logs /app/libs
|
||||
|
||||
# Copy the compiled output to new image
|
||||
COPY --from=builder /conductor/community-server/build/libs/conductor-community-server-*-SNAPSHOT-boot.jar /app/libs/conductor-server.jar
|
||||
COPY ./config.properties /app/config.properties
|
||||
COPY startup.sh /app/
|
||||
RUN chmod +x /app/startup.sh
|
||||
|
||||
HEALTHCHECK --interval=60s --timeout=30s --retries=10 CMD curl -I -XGET http://localhost:8080/health || exit 1
|
||||
|
||||
CMD [ "/app/startup.sh" ]
|
||||
ENTRYPOINT [ "/bin/sh"]
|
|
@ -0,0 +1,24 @@
|
|||
ln -s config.dev/config-pg-es7.properties config.properties
|
||||
docker build -t nubisware/conductor-server:3.13.6-dev -f Dockerfile-server .
|
||||
docker push nubisware/conductor-server:3.13.6-dev
|
||||
unlink config.properties
|
||||
|
||||
# Override fetch plugin with one that uses d4s-boot secure fetch
|
||||
#cp config/fetch.js conductor/ui/src/plugins/fetch.js
|
||||
|
||||
# Override root App with one instantiating d4s-boot configured for dev
|
||||
#cp config.dev/App.jsx conductor/ui/src/App.jsx
|
||||
|
||||
# jump to ui code and build
|
||||
#cd conductor/ui/
|
||||
#yarn install && yarn build
|
||||
#cd -
|
||||
|
||||
# copy the built app to local folder and build Docker image. The clean up.
|
||||
#cp -r conductor/ui/build .
|
||||
#ln -s config.dev/nginx/conf.d/default.conf default.conf
|
||||
#docker build -t nubisware/conductor-frontend:3.13.6-dev -f Dockerfile-frontend .
|
||||
#rm -rf build
|
||||
#unlink default.conf
|
||||
|
||||
#docker push nubisware/conductor-frontend:dev
|
|
@ -0,0 +1,12 @@
|
|||
docker build -t nubisware/conductor-server3:pre -f Dockerfile-server-pre .
|
||||
docker push nubisware/conductor-server3:pre
|
||||
|
||||
#docker build -t nubisware/conductor-frontend:common -f Dockerfile-frontend .
|
||||
|
||||
#cd /home/lettere/git/conductor/ui/
|
||||
#./build-pre-code.sh
|
||||
#cd -
|
||||
#cp -r /home/lettere/git/conductor/ui/build .
|
||||
#docker build -t nubisware/conductor-frontend:pre -f Dockerfile-frontend-pre .
|
||||
#rm -rf build
|
||||
#docker push nubisware/conductor-frontend:pre
|
|
@ -0,0 +1,4 @@
|
|||
git clone https://github.com/Netflix/conductor
|
||||
git clone https://github.com/Netflix/conductor-community
|
||||
|
||||
find conductor-community/ -name dependencies.lock -exec rm -v {} \;
|
|
@ -0,0 +1,12 @@
|
|||
docker build -t nubisware/conductor-server3:prod -f Dockerfile-server-prod .
|
||||
docker push nubisware/conductor-server3:prod
|
||||
|
||||
#docker build -t nubisware/conductor-frontend:common -f Dockerfile-frontend .
|
||||
|
||||
#cd /home/lettere/git/conductor/ui/
|
||||
#./build-prod-code.sh
|
||||
#cd -
|
||||
#cp -r /home/lettere/git/conductor/ui/build .
|
||||
#docker build -t nubisware/conductor-frontend:prod -f Dockerfile-frontend-prod .
|
||||
#rm -rf build
|
||||
#docker push nubisware/conductor-frontend:prod
|
|
@ -0,0 +1,72 @@
|
|||
plugins {
|
||||
id 'org.springframework.boot'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.netflix.conductor:conductor-rest:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-core:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-redis-persistence:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-cassandra-persistence:${revConductor}"
|
||||
|
||||
implementation "com.netflix.conductor:conductor-grpc-server:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-redis-lock:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-redis-concurrency-limit:${revConductor}"
|
||||
|
||||
|
||||
implementation "com.netflix.conductor:conductor-http-task:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-json-jq-task:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-awss3-storage:${revConductor}"
|
||||
implementation "com.netflix.conductor:conductor-awssqs-event-queue:${revConductor}"
|
||||
|
||||
implementation project(':event-queue:conductor-amqp')
|
||||
implementation project(':event-queue:conductor-nats')
|
||||
implementation project(':index:conductor-es7-persistence')
|
||||
implementation project(':external-payload-storage:conductor-azureblob-storage')
|
||||
implementation project(':external-payload-storage:conductor-postgres-external-storage')
|
||||
|
||||
implementation project(':lock:conductor-zookeeper-lock')
|
||||
|
||||
implementation project(':conductor-metrics')
|
||||
|
||||
implementation project(':persistence:conductor-common-persistence')
|
||||
implementation project(':persistence:conductor-postgres-persistence')
|
||||
implementation project(':persistence:conductor-mysql-persistence')
|
||||
|
||||
implementation project(':task:conductor-kafka')
|
||||
|
||||
implementation project(':conductor-workflow-event-listener')
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.retry:spring-retry'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
|
||||
implementation 'org.apache.logging.log4j:log4j-web'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
|
||||
implementation "org.springdoc:springdoc-openapi-ui:${revOpenapi}"
|
||||
|
||||
runtimeOnly "org.glassfish.jaxb:jaxb-runtime:${revJAXB}"
|
||||
|
||||
testImplementation "com.netflix.conductor:conductor-rest:${revConductor}"
|
||||
testImplementation "com.netflix.conductor:conductor-common:${revConductor}"
|
||||
testImplementation "io.grpc:grpc-testing:${revGrpc}"
|
||||
testImplementation "com.google.protobuf:protobuf-java:${revProtoBuf}"
|
||||
testImplementation "io.grpc:grpc-protobuf:${revGrpc}"
|
||||
testImplementation "io.grpc:grpc-stub:${revGrpc}"
|
||||
}
|
||||
|
||||
jar {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
bootJar {
|
||||
mainClass = 'com.netflix.conductor.Conductor'
|
||||
classifier = 'boot'
|
||||
}
|
||||
|
||||
springBoot {
|
||||
buildInfo()
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.98e59355.css",
|
||||
"main.js": "/static/js/main.18fa60f5.js",
|
||||
"index.html": "/index.html",
|
||||
"main.98e59355.css.map": "/static/css/main.98e59355.css.map",
|
||||
"main.18fa60f5.js.map": "/static/js/main.18fa60f5.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.98e59355.css",
|
||||
"static/js/main.18fa60f5.js"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 419.77176 434.76002"
|
||||
version="1.1"
|
||||
id="svg134"
|
||||
sodipodi:docname="favicon.svg"
|
||||
width="419.77176"
|
||||
height="434.76001"
|
||||
inkscape:version="1.1.2 (b8e25be8, 2022-02-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview136"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.2341567"
|
||||
inkscape:cx="208.64449"
|
||||
inkscape:cy="217.15233"
|
||||
inkscape:window-width="1296"
|
||||
inkscape:window-height="932"
|
||||
inkscape:window-x="2544"
|
||||
inkscape:window-y="454"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg134" />
|
||||
<defs
|
||||
id="defs124">
|
||||
<style
|
||||
id="style122">.cls-1{fill:none;}.cls-2{fill:#1976d2;}</style>
|
||||
</defs>
|
||||
<rect
|
||||
class="cls-1"
|
||||
width="565"
|
||||
height="570.42999"
|
||||
id="rect126"
|
||||
x="-73.398232"
|
||||
y="-67.82" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 384.31177,242.99 -59.55,103.19 a 13.52,13.52 0 0 1 -11.67,6.73 h -19.95 l 63.46,-109.92 h -35.47 l -63.45,109.88 h -85.46 a 13.49,13.49 0 0 1 -11.62,-6.69 l -70.490004,-122 a 13.54,13.54 0 0 1 0,-13.48 l 70.450004,-122 a 13.51,13.51 0 0 1 11.67,-6.73 h 67.67 l -13.3,-23.16 a 54.43,54.43 0 0 0 -5.55,-7.6 h -48.83 a 44.3,44.3 0 0 0 -38.26,22.09 l -70.440004,122 a 44.29,44.29 0 0 0 0,44.18 l 70.430004,122 a 44.31,44.31 0 0 0 38.27,22.1 h 140.87 a 44.3,44.3 0 0 0 38.26,-22.09 l 68.42,-118.5 z"
|
||||
id="path128" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 218.88177,398.93 a 55.89,55.89 0 0 1 -23.16,5.12 h -33.54 a 56.31,56.31 0 0 1 -48.58,-28.07 L 38.211766,245.47 a 56.3,56.3 0 0 1 0,-56.14 L 113.60177,58.81 a 56.29,56.29 0 0 1 48.62,-28.07 h 33.54 a 56.28,56.28 0 0 1 48.62,28.07 l 76.79,133 h 35.43 l -63.46,-109.89 h 19.95 a 13.52,13.52 0 0 1 11.67,6.73 l 59.55,103.16 h 35.46 l -68.41,-118.5 a 44.31,44.31 0 0 0 -38.27,-22.13 h -37.68 l -4.47,-7.76 A 87.11,87.11 0 0 0 195.72177,0 h -33.54 A 87.1,87.1 0 0 0 86.971766,43.42 l -75.37,130.55 a 87.07,87.07 0 0 0 0,86.85 l 75.35,130.52 a 87.1,87.1 0 0 0 75.210004,43.42 h 33.54 a 87,87 0 0 0 70.12,-35.83 z"
|
||||
id="path130" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Conductor UI</title><script defer="defer" src="/static/js/main.18fa60f5.js"></script><link href="/static/css/main.98e59355.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
|
@ -0,0 +1 @@
|
|||
<svg version="1.1" id="svg39" width="874.922" height="185.4" xmlns="http://www.w3.org/2000/svg"><defs id="defs11"><style id="style9">.cls-2{fill:#242a36}.cls-3{fill:#1976d2}</style></defs><g id="Layer_2" data-name="Layer 2" transform="translate(-73.828 -68)"><path id="rect13" style="fill:none" d="M0 0h1022.01v320.7H0z"/><path class="cls-2" d="M433.91 140.05q-14.78 0-25 9.56t-10.22 24.58q0 15 10.22 24.65 10.22 9.65 25 9.62a34.71 34.71 0 0 0 24.78-9.62q10.14-9.63 10.15-24.65.01-15.02-10.09-24.58-10.08-9.55-24.84-9.56zm15.15 50.29a21.72 21.72 0 0 1-30.51 0q-6.39-6.27-6.39-16.15 0-9.88 6.39-16.19a21.72 21.72 0 0 1 30.51 0q6.46 6.27 6.46 16.15 0 9.88-6.46 16.19z" id="path15"/><path class="cls-2" d="M515.55 139.66q-14.37 0-22.67 9.88v-8h-13.71v65.38h13.71v-30.23q0-12 5.21-18.18t14.3-6.2a14.28 14.28 0 0 1 11.27 4.88q4.29 4.88 4.28 12.91v36.78h13.84v-39.54q0-12.27-7.25-20t-18.98-7.68z" id="path17"/><path class="cls-2" d="M607 149q-9.09-9.34-23.72-9.35a31 31 0 0 0-22.8 9.75q-9.63 9.75-9.62 24.78.01 15.03 9.59 24.82a31 31 0 0 0 22.8 9.75q14.5 0 23.72-9.22v7.38h13.85V112H607Zm-6.45 41.39a21.06 21.06 0 0 1-15 6.2 20.48 20.48 0 0 1-15.16-6.13q-6.06-6.14-6.06-16.28t6-16.18a20.35 20.35 0 0 1 15.16-6.2 21 21 0 0 1 15 6.26q6.45 6.27 6.45 16.15 0 9.88-6.42 16.19z" id="path19"/><path class="cls-2" d="M682.64 171.68q0 12-5.14 18.2-5.14 6.2-14.23 6.19a14.35 14.35 0 0 1-11.4-4.94q-4.29-4.95-4.28-13V141.5h-13.71v39.41q0 12.39 7.18 20.1 7.18 7.71 19.05 7.71 14.24 0 22.53-9.88v8h13.71V141.5h-13.71z" id="path21"/><path class="cls-2" d="M860.21 140.05q-14.76 0-25 9.56T825 174.19q0 15 10.22 24.65 10.22 9.65 25 9.62a34.67 34.67 0 0 0 24.78-9.62q10.15-9.63 10.15-24.65 0-15.02-10.08-24.58-10.07-9.55-24.86-9.56zm15.16 50.29a21.72 21.72 0 0 1-30.51 0q-6.39-6.27-6.39-16.15 0-9.88 6.39-16.19a21.72 21.72 0 0 1 30.51 0q6.46 6.27 6.46 16.15 0 9.88-6.46 16.19z" id="path23"/><path class="cls-2" d="M944.1 140.71q-15.95 0-24.91 14.76v-14h-13.71v65.38h13.71v-23.04q0-13.83 6.33-21.68t18.48-7.84a36 36 0 0 1 3.82.13l.93-13.18a16.36 16.36 0 0 0-4.65-.53z" id="path25"/><path class="cls-2" d="M347.63 130.18a30.08 30.08 0 0 1 21 9.28 31.21 31.21 0 0 1 7.7 13.48h14.73a44.39 44.39 0 0 0-87.87 9.78q0 19.38 13.38 32.69a43 43 0 0 0 62.07 0 43.7 43.7 0 0 0 12.28-22.27h-14.75a31.37 31.37 0 0 1-7.52 12.86 28.48 28.48 0 0 1-42 0q-9.15-9.44-9.16-23.27-.01-13.83 9.16-23.26a30.08 30.08 0 0 1 20.98-9.29z" id="path27"/><path class="cls-2" d="M740.55 152.31a20.21 20.21 0 0 1 19.85 14.1h13.91a31.94 31.94 0 0 0-9.31-17 35.54 35.54 0 0 0-48.91 0Q706 159.16 706 174.19q0 15.03 10.09 24.81a35.7 35.7 0 0 0 48.91 0 31.68 31.68 0 0 0 9.23-16.67h-13.88a19.45 19.45 0 0 1-4.65 7.71 20.38 20.38 0 0 1-15.15 6q-9.24 0-15.16-6t-5.93-15.88q0-9.62 6-15.75a20.23 20.23 0 0 1 15.09-6.1z" id="path29"/><path class="cls-2" d="M808.24 195.81a10.32 10.32 0 0 1-7.91-3.1q-2.9-3.11-2.9-9v-30.87h22.41V141.5h-22.41v-20.3h-13.7v63.4q0 11.86 6.32 18 6.32 6.14 17.27 6.13 7.77 0 16.21-5l-4.22-11.46a19.91 19.91 0 0 1-11.07 3.54z" id="path31"/><path class="cls-3" d="m237.69 171.63-25.39 44a5.75 5.75 0 0 1-5 2.88h-8.51l27.06-46.86h-15.1l-27.06 46.86h-36.44a5.76 5.76 0 0 1-5-2.88l-30-52a5.74 5.74 0 0 1 0-5.75l30-52a5.75 5.75 0 0 1 5-2.87h28.86l-5.69-9.86a23 23 0 0 0-2.37-3.24h-20.8a18.88 18.88 0 0 0-16.31 9.42l-30 52a18.87 18.87 0 0 0 0 18.84l30 52a18.91 18.91 0 0 0 16.32 9.42h60.07a18.91 18.91 0 0 0 16.32-9.42l29.17-50.53z" id="path33"/><path class="cls-3" d="M167.15 238.13a23.94 23.94 0 0 1-9.88 2.18H143a24 24 0 0 1-20.73-12l-32.16-55.62a24 24 0 0 1 0-23.94l32.13-55.66a24 24 0 0 1 20.73-12h14.3a24 24 0 0 1 20.74 12l32.74 56.71h15.12L198.81 103h8.51a5.76 5.76 0 0 1 5 2.87l25.39 44h15.12l-29.19-50.6a18.91 18.91 0 0 0-16.32-9.42h-16.07l-1.9-3.31A37.15 37.15 0 0 0 157.27 68H143a37.12 37.12 0 0 0-32.1 18.54L78.77 142.2a37.1 37.1 0 0 0 0 37l32.13 55.66A37.12 37.12 0 0 0 143 253.4h14.3a37.11 37.11 0 0 0 29.9-15.27z" id="path35"/></g></svg>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
/*! Hammer.JS - v2.0.17-rc - 2019-12-16
|
||||
* http://naver.github.io/egjs
|
||||
*
|
||||
* Forked By Naver egjs
|
||||
* Copyright (c) hammerjs
|
||||
* Licensed under the MIT license */
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2012-2013 Chris Pettitt
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Lodash <https://lodash.com/>
|
||||
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
||||
* Released under MIT license <https://lodash.com/license>
|
||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
*/
|
||||
|
||||
/**
|
||||
* A better abstraction over CSS.
|
||||
*
|
||||
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
||||
* @website https://github.com/cssinjs/jss
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* vis-timeline and vis-graph2d
|
||||
* https://visjs.github.io/vis-timeline/
|
||||
*
|
||||
* Create a fully customizable, interactive timeline with items and ranges.
|
||||
*
|
||||
* @version 7.7.0
|
||||
* @date 2022-07-10T21:34:08.601Z
|
||||
*
|
||||
* @copyright (c) 2011-2017 Almende B.V, http://almende.com
|
||||
* @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs
|
||||
*
|
||||
* @license
|
||||
* vis.js is dual licensed under both
|
||||
*
|
||||
* 1. The Apache 2.0 License
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* and
|
||||
*
|
||||
* 2. The MIT License
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* vis.js may be distributed under either license.
|
||||
*/
|
||||
|
||||
/** @license React v0.19.1
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.14.0
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.14.0
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.14.0
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
//! moment.js
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef",
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.dev.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.dev.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/by-tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -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
|
|
@ -13,6 +13,7 @@ spring.datasource.hikari.minimum-idle=2
|
|||
conductor.indexing.enabled=true
|
||||
conductor.elasticsearch.version=7
|
||||
conductor.elasticsearch.url=http://es:9200
|
||||
conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
|
@ -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 conductor.dev.d4science.org;
|
||||
|
||||
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.dev.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.dev.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.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
gunzip on;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef",
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.pre.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.pre.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/by-tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[common]
|
||||
loglevel = info
|
||||
threads = 1
|
||||
pollrate = 1
|
||||
|
||||
[pymail]
|
||||
server = smtp-relay.d4science.org
|
||||
user = conductor_pre
|
||||
password =
|
||||
protocol = starttls
|
||||
port = 587
|
|
@ -0,0 +1,26 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgres:5432/conductor
|
||||
spring.datasource.username=conductor
|
||||
spring.datasource.password=conductor
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
conductor.indexing.enabled=true
|
||||
conductor.elasticsearch.version=7
|
||||
conductor.elasticsearch.url=http://es:9200
|
||||
conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -0,0 +1,88 @@
|
|||
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.pre.d4science.org;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect";
|
||||
|
||||
proxy_ignore_headers Cache-Control Expires Set-Cookie;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
|
||||
proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef",
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.pre.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/by-tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[common]
|
||||
loglevel = info
|
||||
threads = 1
|
||||
pollrate = 1
|
||||
|
||||
[pymail]
|
||||
server = smtp-relay.d4science.org
|
||||
user = conductor_prod
|
||||
password =
|
||||
protocol = starttls
|
||||
port = 587
|
|
@ -0,0 +1,26 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgresql-srv.d4science.org:5432/conductor
|
||||
spring.datasource.username=conductor_u
|
||||
spring.datasource.password=c36dda661add7c2b5093087ddb655992
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
conductor.indexing.enabled=true
|
||||
conductor.elasticsearch.version=7
|
||||
conductor.elasticsearch.url=http://es:9200
|
||||
conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -0,0 +1,88 @@
|
|||
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.d4science.org;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect";
|
||||
|
||||
proxy_ignore_headers Cache-Control Expires Set-Cookie;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
|
||||
proxy_pass "https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import { useEnv } from "./env";
|
||||
|
||||
export function useFetchContext() {
|
||||
const { stack } = useEnv();
|
||||
return {
|
||||
stack,
|
||||
ready: true,
|
||||
};
|
||||
}
|
||||
export function fetchWithContext(
|
||||
path,
|
||||
context,
|
||||
fetchParams,
|
||||
isJsonResponse = true
|
||||
) {
|
||||
const newParams = { ...fetchParams };
|
||||
|
||||
const newPath = `/api/${path}`;
|
||||
const cleanPath = newPath.replace(/([^:]\/)\/+/g, "$1"); // Cleanup duplicated slashes
|
||||
|
||||
const boot = document.querySelector("d4s-boot-2")
|
||||
|
||||
return boot.secureFetch(cleanPath, newParams)
|
||||
.then((res) => Promise.all([res, res.text()]))
|
||||
.then(([res, text]) => {
|
||||
if (!res.ok) {
|
||||
// get error message from body or default to response status
|
||||
const error = text || res.status;
|
||||
return Promise.reject(error);
|
||||
} else if (!text || text.length === 0) {
|
||||
return null;
|
||||
} else if (!isJsonResponse) {
|
||||
return text;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
export default { config };
|
||||
|
||||
var config = {
|
||||
"hosts" : [
|
||||
{
|
||||
"host": ["conductor.d4science.org", "conductor.pre.d4science.org", "conductor.dev.d4science.org", "conductor.int.d4science.net", "conductor"],
|
||||
"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"],
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# 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;
|
||||
}
|
||||
|
||||
env pep_credentials;
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
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){
|
||||
if(process.env["pep_credentials"] || 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=>{
|
||||
//compare for both string and array of strings
|
||||
return ((h.host.filter && h.host.indexOf(host) !== -1) || h.host === host)
|
||||
})
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
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
|
||||
volumes:
|
||||
- es_data:/usr/share/elasticsearch/data
|
||||
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:dev"
|
||||
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:dev"
|
||||
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}
|
||||
smtp_user: ${smtp_user}
|
||||
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_dev"
|
||||
es_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: nfs4
|
||||
o: "nfsvers=4,addr=146.48.123.250,rw"
|
||||
device: ":/nfs/conductor_es_dev"
|
|
@ -0,0 +1,122 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
environment:
|
||||
- POSTGRES_USER=conductor
|
||||
- POSTGRES_PASSWORD=conductor
|
||||
networks:
|
||||
- conductor-network
|
||||
ports:
|
||||
- "5432:5432"
|
||||
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
|
||||
ports:
|
||||
- "9200:9200"
|
||||
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:
|
||||
image: "nubisware/conductor-server:3.13.6-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:3.13.6-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:dev'
|
||||
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:dev'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
logging:
|
||||
driver: "journald"
|
||||
|
||||
networks:
|
||||
conductor-network:
|
|
@ -0,0 +1,153 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
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
|
||||
volumes:
|
||||
- es_data:/usr/share/elasticsearch/data
|
||||
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}
|
||||
smtp_user: ${smtp_user}
|
||||
image: 'nubisware/nubisware-conductor-worker-py-d4s'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
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
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
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"
|
||||
es_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: nfs4
|
||||
o: "nfsvers=4,addr=146.48.123.250,rw"
|
||||
device: ":/nfs/conductor_es_pre"
|
|
@ -0,0 +1,122 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
es:
|
||||
image: elasticsearch:7.6.2
|
||||
volumes:
|
||||
- es_data:/usr/share/elasticsearch/data
|
||||
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:prod"
|
||||
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:prod"
|
||||
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}
|
||||
smtp_user: ${smtp_user}
|
||||
image: 'nubisware/nubisware-conductor-worker-py-d4s'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
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: 4
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
logging:
|
||||
driver: "journald"
|
||||
|
||||
networks:
|
||||
conductor-network:
|
||||
haproxy-public:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
es_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: nfs4
|
||||
o: "nfsvers=4,addr=146.48.123.250,rw"
|
||||
device: ":/nfs/conductor_es_prod"
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright 2021 Netflix, Inc.
|
||||
# <p>
|
||||
# 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
|
||||
# <p>
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# <p>
|
||||
# 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.
|
||||
#
|
||||
|
||||
# startup.sh - startup script for the server docker image
|
||||
|
||||
echo "Starting Conductor server"
|
||||
|
||||
if curl es:9200; then echo ES found; else exit 1; fi;
|
||||
|
||||
# Start the server
|
||||
cd /app/libs
|
||||
config_file=/app/config.properties
|
||||
|
||||
echo "Using java options config: $JAVA_OPTS"
|
||||
|
||||
java ${JAVA_OPTS} -jar -DCONDUCTOR_CONFIG_FILE=$config_file conductor-server.jar 2>&1 | tee -a /app/logs/server.log
|
|
@ -0,0 +1,4 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
#Mon Oct 09 18:00:18 CEST 2023
|
||||
gradle.version=7.4.2
|
|
@ -0,0 +1,21 @@
|
|||
FROM nginx:alpine
|
||||
|
||||
LABEL maintainer="Nubisware <info@nubisware.com>"
|
||||
|
||||
# Bake common configurations for Conductor PEP
|
||||
COPY config/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY config/nginx/pep.js /etc/nginx/pep.js
|
||||
COPY config/nginx/config.js /etc/nginx/config.js
|
||||
|
||||
# Ensure that cache is invalidated
|
||||
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
|
||||
|
||||
# Copy compiled UI assets to nginx www directory
|
||||
WORKDIR /usr/share/nginx/html
|
||||
RUN rm -rf ./*
|
||||
COPY build/ .
|
||||
|
||||
# Copy NGINX default configuration
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
|
||||
# ===========================================================================================================
|
||||
# 0. Builder stage
|
||||
# ===========================================================================================================
|
||||
FROM eclipse-temurin:11-jdk-focal AS builder
|
||||
|
||||
LABEL maintainer="Nubisware SRL"
|
||||
|
||||
# Copy the project directly onto the image
|
||||
COPY ./conductor-community /conductor
|
||||
WORKDIR /conductor
|
||||
|
||||
# Build the server on run
|
||||
RUN ./gradlew generateLock updateLock saveLock
|
||||
RUN ./gradlew build -x test --stacktrace
|
||||
|
||||
# ===========================================================================================================
|
||||
# 1. Bin stage
|
||||
# ===========================================================================================================
|
||||
FROM eclipse-temurin:11-jre-focal
|
||||
|
||||
LABEL maintainer="Nubisware SRL"
|
||||
|
||||
# Make app folders
|
||||
RUN mkdir -p /app/config /app/logs /app/libs
|
||||
|
||||
# Copy the compiled output to new image
|
||||
COPY --from=builder /conductor/community-server/build/libs/conductor-community-server-*-SNAPSHOT-boot.jar /app/libs/conductor-server.jar
|
||||
COPY ./config.properties /app/config.properties
|
||||
COPY ./startup.sh /app/startup.sh
|
||||
|
||||
HEALTHCHECK --interval=60s --timeout=30s --retries=10 CMD curl -I -XGET http://localhost:8080/health || exit 1
|
||||
|
||||
CMD [ "/app/startup.sh" ]
|
||||
ENTRYPOINT [ "/bin/sh"]
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
tag='dev'
|
||||
else
|
||||
tag=$1
|
||||
fi
|
||||
|
||||
CONDUCTOR_VERSION=v3.13.8
|
||||
|
||||
rm -rf conductor conductor-community
|
||||
git clone https://github.com/Netflix/conductor -b $CONDUCTOR_VERSION
|
||||
git clone https://github.com/Netflix/conductor-community -b $CONDUCTOR_VERSION
|
||||
find conductor-community/ -name dependencies.lock -exec rm -v {} \;
|
||||
|
||||
# Build conductor server
|
||||
ln -s config.$tag/config.properties config.properties
|
||||
ln -s config/startup.sh startup.sh
|
||||
docker build --no-cache -t hub.dev.d4science.org/conductor/server:$CONDUCTOR_VERSION-$tag -f Dockerfile-server .
|
||||
docker push hub.dev.d4science.org/conductor/server:$CONDUCTOR_VERSION-$tag
|
||||
unlink config.properties
|
||||
unlink startup.sh
|
||||
|
||||
exit 0
|
||||
|
||||
# Override fetch plugin with one that uses d4s-boot secure fetch
|
||||
cp config/fetch.js conductor/ui/src/plugins/fetch.js
|
||||
|
||||
# Override root App with one instantiating d4s-boot configured for dev
|
||||
cp config.$tag/App.jsx conductor/ui/src/App.jsx
|
||||
|
||||
# jump to ui code and build
|
||||
cd conductor/ui/
|
||||
yarn install && yarn build
|
||||
cd -
|
||||
|
||||
# copy the built app to local folder and build Docker image. The clean up.
|
||||
cp -r conductor/ui/build .
|
||||
ln -s config.$tag/nginx/conf.d/default.conf default.conf
|
||||
docker build --no-cache -t hub.dev.d4science.org/conductor/frontend:$CONDUCTOR_VERSION-$tag -f Dockerfile-frontend .
|
||||
#docker push hub.dev.d4science.org/conductor/frontend:$CONDUCTOR_VERSION-$tag
|
||||
rm -rf build
|
||||
unlink default.conf
|
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef", // TODO: Use theme var
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.dev.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.dev.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef",
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.dev.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.dev.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/by-tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,30 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgres:5432/conductor
|
||||
spring.datasource.username=conductor
|
||||
spring.datasource.password=conductor
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=20
|
||||
#spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Use Postgres for indexing
|
||||
conductor.indexing.enabled=true
|
||||
conductor.indexing.type=postgres
|
||||
conductor.elasticsearch.version=postgres
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
#conductor.elasticsearch.version=7
|
||||
#conductor.elasticsearch.url=http://es:9200
|
||||
#conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -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 conductor.dev.d4science.org conductor.cloud-dev.d4science.org;
|
||||
|
||||
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.dev.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.dev.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.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
gunzip on;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef",
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.pre.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.pre.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/by-tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[common]
|
||||
loglevel = info
|
||||
threads = 1
|
||||
pollrate = 1
|
||||
|
||||
[pymail]
|
||||
server = smtp-relay.d4science.org
|
||||
user = conductor_pre
|
||||
password =
|
||||
protocol = starttls
|
||||
port = 587
|
|
@ -0,0 +1,26 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgres:5432/conductor
|
||||
spring.datasource.username=conductor
|
||||
spring.datasource.password=conductor
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
conductor.indexing.enabled=true
|
||||
conductor.elasticsearch.version=7
|
||||
conductor.elasticsearch.url=http://es:9200
|
||||
conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -0,0 +1,88 @@
|
|||
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.pre.d4science.org;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect";
|
||||
|
||||
proxy_ignore_headers Cache-Control Expires Set-Cookie;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
|
||||
proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.pre.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import { Button, AppBar, Toolbar } from "@material-ui/core";
|
||||
import AppLogo from "./plugins/AppLogo";
|
||||
import NavLink from "./components/NavLink";
|
||||
|
||||
import WorkflowSearch from "./pages/executions/WorkflowSearch";
|
||||
import TaskSearch from "./pages/executions/TaskSearch";
|
||||
|
||||
import Execution from "./pages/execution/Execution";
|
||||
import WorkflowDefinitions from "./pages/definitions/Workflow";
|
||||
import WorkflowDefinition from "./pages/definition/WorkflowDefinition";
|
||||
import TaskDefinitions from "./pages/definitions/Task";
|
||||
import TaskDefinition from "./pages/definition/TaskDefinition";
|
||||
import EventHandlerDefinitions from "./pages/definitions/EventHandler";
|
||||
import EventHandlerDefinition from "./pages/definition/EventHandler";
|
||||
import TaskQueue from "./pages/misc/TaskQueue";
|
||||
import KitchenSink from "./pages/kitchensink/KitchenSink";
|
||||
import DiagramTest from "./pages/kitchensink/DiagramTest";
|
||||
import Examples from "./pages/kitchensink/Examples";
|
||||
import Gantt from "./pages/kitchensink/Gantt";
|
||||
|
||||
import CustomRoutes from "./plugins/CustomRoutes";
|
||||
import AppBarModules from "./plugins/AppBarModules";
|
||||
import CustomAppBarButtons from "./plugins/CustomAppBarButtons";
|
||||
import Workbench from "./pages/workbench/Workbench";
|
||||
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
backgroundColor: "#efefef",
|
||||
display: "flex",
|
||||
},
|
||||
body: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
paddingTop: theme.overrides.MuiAppBar.root.height,
|
||||
},
|
||||
toolbarRight: {
|
||||
marginLeft: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
},
|
||||
toolbarRegular: {
|
||||
minHeight: 80,
|
||||
},
|
||||
}));
|
||||
|
||||
class AppAuth extends Component{
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<script src="https://cdn.cloud.d4science.org/boot/d4s-boot.js"></script>
|
||||
</Helmet>
|
||||
<d4s-boot-2 url="https://accounts.d4science.org/auth" redirect-url="http://localhost/login/callback" gateway="conductor-ui">
|
||||
</d4s-boot-2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppBody extends Component{
|
||||
constructor(props){
|
||||
super(props)
|
||||
this.state = { open : false }
|
||||
}
|
||||
|
||||
setOpen(v){
|
||||
this.setState({ open : v })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("authenticated", ev=>{
|
||||
this.setOpen(true)
|
||||
})
|
||||
}
|
||||
|
||||
render(){
|
||||
const classes = this.props.classes;
|
||||
return !this.state.open ? <div></div> : (
|
||||
<div className={classes.root}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
classes={{
|
||||
regular: classes.toolbarRegular,
|
||||
}}
|
||||
>
|
||||
<AppLogo />
|
||||
<Button component={NavLink} path="/">
|
||||
Executions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workflowDefs">
|
||||
Definitions
|
||||
</Button>
|
||||
<Button component={NavLink} path="/taskQueue">
|
||||
Task Queues
|
||||
</Button>
|
||||
<Button component={NavLink} path="/workbench">
|
||||
Workbench
|
||||
</Button>
|
||||
<CustomAppBarButtons />
|
||||
|
||||
<div className={classes.toolbarRight}>
|
||||
<AppBarModules />
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div className={classes.body}>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<WorkflowSearch />
|
||||
</Route>
|
||||
<Route exact path="/search/by-tasks">
|
||||
<TaskSearch />
|
||||
</Route>
|
||||
<Route path="/execution/:id/:taskId?">
|
||||
<Execution />
|
||||
</Route>
|
||||
<Route exact path="/workflowDefs">
|
||||
<WorkflowDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/workflowDef/:name?/:version?">
|
||||
<WorkflowDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskDefs">
|
||||
<TaskDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/taskDef/:name?">
|
||||
<TaskDefinition />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef">
|
||||
<EventHandlerDefinitions />
|
||||
</Route>
|
||||
<Route exact path="/eventHandlerDef/:name">
|
||||
<EventHandlerDefinition />
|
||||
</Route>
|
||||
<Route exact path="/taskQueue/:name?">
|
||||
<TaskQueue />
|
||||
</Route>
|
||||
<Route exact path="/workbench">
|
||||
<Workbench />
|
||||
</Route>
|
||||
<Route exact path="/kitchen">
|
||||
<KitchenSink />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/diagram">
|
||||
<DiagramTest />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/examples">
|
||||
<Examples />
|
||||
</Route>
|
||||
<Route exact path="/kitchen/gantt">
|
||||
<Gantt />
|
||||
</Route>
|
||||
<CustomRoutes />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AppContent extends Component{
|
||||
render(){
|
||||
return(
|
||||
<div>
|
||||
<AppAuth/>
|
||||
<AppBody classes={this.props.classes}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Keep functional constructor to avoid problems with useStyles
|
||||
export default function App() {
|
||||
const classes = useStyles();
|
||||
|
||||
return <AppContent classes={classes}/>
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[common]
|
||||
loglevel = info
|
||||
threads = 1
|
||||
pollrate = 1
|
||||
|
||||
[pymail]
|
||||
server = smtp-relay.d4science.org
|
||||
user = conductor_prod
|
||||
password =
|
||||
protocol = starttls
|
||||
port = 587
|
|
@ -0,0 +1,26 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgresql-srv.d4science.org:5432/conductor
|
||||
spring.datasource.username=conductor_u
|
||||
spring.datasource.password=c36dda661add7c2b5093087ddb655992
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
conductor.indexing.enabled=true
|
||||
conductor.elasticsearch.version=7
|
||||
conductor.elasticsearch.url=http://es:9200
|
||||
conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -0,0 +1,30 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgres:5432/conductor
|
||||
spring.datasource.username=conductor
|
||||
spring.datasource.password=conductor
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Use Postgres for indexing
|
||||
conductor.indexing.enabled=true
|
||||
conductor.indexing.type=postgres
|
||||
conductor.elasticsearch.version=postgres
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
#conductor.elasticsearch.version=7
|
||||
#conductor.elasticsearch.url=http://es:9200
|
||||
#conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -0,0 +1,88 @@
|
|||
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.d4science.org conductor.cloud.d4science.org;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token/introspect";
|
||||
|
||||
proxy_ignore_headers Cache-Control Expires Set-Cookie;
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
|
||||
proxy_pass "https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
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_set_header Accept-Encoding identity;
|
||||
proxy_pass "https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# In memory database persistence model.
|
||||
conductor.db.type=memory
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
conductor.indexing.enabled=false
|
||||
conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
|
@ -0,0 +1,42 @@
|
|||
import { useEnv } from "./env";
|
||||
|
||||
export function useFetchContext() {
|
||||
const { stack } = useEnv();
|
||||
return {
|
||||
stack,
|
||||
ready: true,
|
||||
};
|
||||
}
|
||||
export function fetchWithContext(
|
||||
path,
|
||||
context,
|
||||
fetchParams,
|
||||
isJsonResponse = true
|
||||
) {
|
||||
const newParams = { ...fetchParams };
|
||||
|
||||
const newPath = `/api/${path}`;
|
||||
const cleanPath = newPath.replace(/([^:]\/)\/+/g, "$1"); // Cleanup duplicated slashes
|
||||
|
||||
const boot = document.querySelector("d4s-boot-2")
|
||||
|
||||
return boot.secureFetch(cleanPath, newParams)
|
||||
.then((res) => Promise.all([res, res.text()]))
|
||||
.then(([res, text]) => {
|
||||
if (!res.ok) {
|
||||
// get error message from body or default to response status
|
||||
const error = text || res.status;
|
||||
return Promise.reject(error);
|
||||
} else if (!text || text.length === 0) {
|
||||
return null;
|
||||
} else if (!isJsonResponse) {
|
||||
return text;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
export default { config };
|
||||
|
||||
var config = {
|
||||
"hosts" : [
|
||||
{
|
||||
"host": ["conductor.d4science.org", "conductor.pre.d4science.org", "conductor.dev.d4science.org", "conductor.cloud-dev.d4science.org", "conductor.cloud.d4science.org", "conductor"],
|
||||
"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"],
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# 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;
|
||||
}
|
||||
|
||||
env pep_credentials;
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
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){
|
||||
if(process.env["pep_credentials"] || 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.responseText);
|
||||
if (response.active === true) {
|
||||
return response
|
||||
} else {
|
||||
throw new Error("Unauthorized: " + reply.responseText)
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unauthorized: " + reply.responseText)
|
||||
}
|
||||
}).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.responseText);
|
||||
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.responseText);
|
||||
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.responseText))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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=>{
|
||||
//compare for both string and array of strings
|
||||
return ((h.host.filter && h.host.indexOf(host) !== -1) || h.host === host)
|
||||
})
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright 2021 Netflix, Inc.
|
||||
# <p>
|
||||
# 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
|
||||
# <p>
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# <p>
|
||||
# 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.
|
||||
#
|
||||
|
||||
# startup.sh - startup script for the server docker image
|
||||
|
||||
echo "Starting Conductor server"
|
||||
|
||||
# Start the server
|
||||
cd /app/libs
|
||||
config_file=/app/config.properties
|
||||
|
||||
echo "Using java options config: $JAVA_OPTS"
|
||||
|
||||
java ${JAVA_OPTS} -jar -DCONDUCTOR_CONFIG_FILE=$config_file conductor-server.jar 2>&1 | tee -a /app/logs/server.log
|
|
@ -0,0 +1,117 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
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"
|
||||
|
||||
conductor-server:
|
||||
environment:
|
||||
- CONFIG_PROP=config.properties
|
||||
image: "hub.dev.d4science.org/conductor/server:v3.13.8-dev"
|
||||
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: "hub.dev.d4science.org/conductor/frontend:v3.13.8-dev"
|
||||
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}
|
||||
smtp_user: ${smtp_user}
|
||||
image: 'hub.dev.d4science.org/conductor/worker:dev'
|
||||
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: 'hub.dev.d4science.org/conductor/worker:dev'
|
||||
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:
|
||||
# These settings are relative to dev-cloud
|
||||
pg_db_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: nfs4
|
||||
o: "nfsvers=4,addr=192.168.1.222,rw"
|
||||
device: "/nfs_conductor_pg_dev"
|
|
@ -0,0 +1,98 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
environment:
|
||||
- POSTGRES_USER=conductor
|
||||
- POSTGRES_PASSWORD=conductor
|
||||
networks:
|
||||
- conductor-network
|
||||
ports:
|
||||
- "5432:5432"
|
||||
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"
|
||||
|
||||
conductor-server:
|
||||
image: "hub.dev.d4science.org/conductor/server:v3.13.8-dev"
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
volumes:
|
||||
- /home/lettere/git/isti/conductor-setup/v3.13.8/testconfig.properties:/app/config.properties
|
||||
logging:
|
||||
driver: "journald"
|
||||
|
||||
pep:
|
||||
image: "hub.dev.d4science.org/conductor/frontend:v3.13.8-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: "Ansible Eval Mail HttpBridge"
|
||||
smtp_pass: ${smtp_pass}
|
||||
image: 'hub.dev.d4science.org/conductor/worker:dev'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 4
|
||||
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: 'hub.dev.d4science.org/conductor/worker:dev'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 6
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
logging:
|
||||
driver: "journald"
|
||||
|
||||
networks:
|
||||
conductor-network:
|
|
@ -0,0 +1,153 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
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
|
||||
volumes:
|
||||
- es_data:/usr/share/elasticsearch/data
|
||||
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}
|
||||
smtp_user: ${smtp_user}
|
||||
image: 'nubisware/nubisware-conductor-worker-py-d4s'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
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
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
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"
|
||||
es_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: nfs4
|
||||
o: "nfsvers=4,addr=146.48.123.250,rw"
|
||||
device: ":/nfs/conductor_es_pre"
|
|
@ -0,0 +1,120 @@
|
|||
version: '3.6'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
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: 15s
|
||||
timeout: 15s
|
||||
retries: 120
|
||||
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: "hub.dev.d4science.org/conductor/server:v3.13.8-prod"
|
||||
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: "hub.dev.d4science.org/conductor/frontend:v3.13.8-prod"
|
||||
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}
|
||||
smtp_user: ${smtp_user}
|
||||
image: 'hub.dev.d4science.org/conductor/worker:prod'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
logging:
|
||||
driver: "journald"
|
||||
|
||||
pyrestworkers:
|
||||
environment:
|
||||
CONDUCTOR_SERVER: http://conductor-server:8080/api/
|
||||
CONDUCTOR_HEALTH: http://conductor-server:8080/health
|
||||
worker_plugins: Http
|
||||
image: 'hub.dev.d4science.org/conductor/worker:prod'
|
||||
networks:
|
||||
- conductor-network
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 4
|
||||
restart_policy:
|
||||
condition: any
|
||||
delay: 5s
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node.role == worker]
|
||||
logging:
|
||||
driver: "journald"
|
||||
|
||||
networks:
|
||||
conductor-network:
|
||||
haproxy-public:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
pg_db_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: nfs4
|
||||
o: "nfsvers=4,addr=192.168.1.222,rw"
|
||||
device: ":/nfs_conductor_pg_prod"
|
|
@ -0,0 +1,31 @@
|
|||
# Database persistence type.
|
||||
conductor.db.type=postgres
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://postgres:5432/conductor
|
||||
spring.datasource.username=conductor
|
||||
spring.datasource.password=conductor
|
||||
|
||||
# Hikari pool sizes are -1 by default and prevent startup
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
#spring.datasource.hikari.minimum-idle=2
|
||||
|
||||
# Use Postgres for indexing
|
||||
conductor.indexing.enabled=true
|
||||
conductor.indexing.type=postgres
|
||||
conductor.elasticsearch.version=postgres
|
||||
|
||||
# Elastic search instance indexing is disabled.
|
||||
#conductor.elasticsearch.version=7
|
||||
#conductor.elasticsearch.url=http://es:9200
|
||||
#conductor.elasticsearch.clusterHealthColor=yellow
|
||||
|
||||
#Enable Prometheus
|
||||
conductor.metrics-prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=prometheus,health,info,metrics
|
||||
|
||||
# GRPC disabled
|
||||
conductor.grpc-server.enabled=false
|
||||
|
||||
# Load sample kitchen sink disabled
|
||||
loadSample=false
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue