diff --git a/.github/workflows/deploy_images.yml b/.github/workflows/deploy_images.yml index 324ac44428..6d353d0a9a 100644 --- a/.github/workflows/deploy_images.yml +++ b/.github/workflows/deploy_images.yml @@ -33,6 +33,11 @@ on: default: true description: Build new image of save-orchestrator required: false + sandbox: + type: boolean + default: true + description: Build new image of save-sandbox + required: false preprocessor: type: boolean default: true @@ -57,7 +62,7 @@ jobs: name: Prepare to build working-directory: save-cloud run: | - echo GRADLE_TASKS=:api-gateway:bootBuildImage :save-backend:bootBuildImage :save-frontend:buildImage :save-orchestrator:bootBuildImage :save-preprocessor:bootBuildImage >> $GITHUB_ENV + echo GRADLE_TASKS=:api-gateway:bootBuildImage :save-backend:bootBuildImage :save-frontend:buildImage :save-orchestrator:bootBuildImage :save-sandbox:bootBuildImage :save-preprocessor:bootBuildImage >> $GITHUB_ENV - if: github.event_name == 'workflow_dispatch' name: Prepare to build from branch working-directory: save-cloud @@ -69,6 +74,7 @@ jobs: if ${{ inputs.backend }} == 'true'; then GRADLE_TASKS+=":save-backend:bootBuildImage "; fi if ${{ inputs.frontend }} == 'true'; then GRADLE_TASKS+=":save-frontend:buildImage "; fi if ${{ inputs.orchestrator }} == 'true'; then GRADLE_TASKS+=":save-orchestrator:bootBuildImage "; fi + if ${{ inputs.sandbox }} == 'true'; then GRADLE_TASKS+=":save-sandbox:bootBuildImage "; fi if ${{ inputs.preprocessor }} == 'true'; then GRADLE_TASKS+=":save-preprocessor:bootBuildImage "; fi echo GRADLE_TASKS=$GRADLE_TASKS >> $GITHUB_ENV - name: checkout save-core diff --git a/api-gateway/src/main/resources/application-dev.yml b/api-gateway/src/main/resources/application-dev.yml index 75f4c08223..50bc9bc621 100644 --- a/api-gateway/src/main/resources/application-dev.yml +++ b/api-gateway/src/main/resources/application-dev.yml @@ -3,6 +3,8 @@ gateway: url: http://localhost:5800 frontend: url: http://localhost:5810 + sandbox: + url: http://localhost:5400 spring: security: oauth2: diff --git a/api-gateway/src/main/resources/application.yml b/api-gateway/src/main/resources/application.yml index 9878209035..022a00903a 100644 --- a/api-gateway/src/main/resources/application.yml +++ b/api-gateway/src/main/resources/application.yml @@ -9,6 +9,8 @@ gateway: url: http://frontend:5810 grafana: url: http://grafana:9100 + sandbox: + url: http://sandbox:5400 management: endpoints: web: @@ -50,6 +52,14 @@ spring: uri: ${gateway.grafana.url} predicates: - Path=/grafana/** + - id: sandbox-api_route + uri: ${gateway.sandbox.url} + predicates: + - Path=/sandbox/api/** + filters: + # If SESSION cookie is passed to downstream, it is then removed, because downstream discards it + - RemoveRequestHeader=Cookie + - ConvertAuthorizationHeader= --- spring: diff --git a/build.gradle.kts b/build.gradle.kts index 97b8ac4575..1b62f4ae7f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,24 +9,27 @@ plugins { } val profile = properties.getOrDefault("save.profile", "dev") as String -val databaseCredentials = getDatabaseCredentials(profile) liquibase { activities { + val commonArguments = mapOf( + "logLevel" to "info", + "contexts" to when (profile) { + "prod" -> "prod" + "dev" -> "dev" + else -> throw GradleException("Profile $profile not configured to map on a particular liquibase context") + } + ) // Configuring luiquibase register("main") { - arguments = mapOf( - "changeLogFile" to "db/db.changelog-master.xml", - "url" to databaseCredentials.databaseUrl, - "username" to databaseCredentials.username, - "password" to databaseCredentials.password, - "logLevel" to "info", - "contexts" to when (profile) { - "prod" -> "prod" - "dev" -> "dev" - else -> throw GradleException("Profile $profile not configured to map on a particular liquibase context") - } - ) + arguments = mapOf("changeLogFile" to "db/db.changelog-master.xml") + + getBackendDatabaseCredentials(profile).toLiquibaseArguments() + + commonArguments + } + register("sandbox") { + arguments = mapOf("changeLogFile" to "save-sandbox/db/db.changelog-sandbox.xml") + + getSandboxDatabaseCredentials(profile).toLiquibaseArguments() + + commonArguments } } } diff --git a/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DatabaseUtils.kt b/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DatabaseUtils.kt index 2ba556dba3..3295fe6161 100644 --- a/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DatabaseUtils.kt +++ b/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DatabaseUtils.kt @@ -14,41 +14,52 @@ data class DatabaseCredentials( val databaseUrl: String, val username: String, val password: String -) +) { + /** + * @return arguments for liquibase task + */ + fun toLiquibaseArguments(): Map = mapOf( + "url" to "$databaseUrl?createDatabaseIfNotExist=true", + "username" to username, + "password" to password, + ) +} + +/** + * @param profile a profile to get credentials for + * @return an instance of [DatabaseCredentials] for [profile] in backend + */ +fun Project.getBackendDatabaseCredentials(profile: String): DatabaseCredentials = getDatabaseCredentials("save-backend", profile) /** * @param profile a profile to get credentials for - * @return an instance of [DatabaseCredentials] for [profile] + * @return an instance of [DatabaseCredentials] for [profile] in sandbox */ -@Suppress("AVOID_NULL_CHECKS") -fun Project.getDatabaseCredentials(profile: String): DatabaseCredentials { +fun Project.getSandboxDatabaseCredentials(profile: String): DatabaseCredentials = getDatabaseCredentials("save-sandbox", profile) + +private fun Project.getDatabaseCredentials(projectName: String, profile: String): DatabaseCredentials { val props = java.util.Properties() + // Branch for other environments, e.g. local deployment or server deployment + file("$projectName/src/main/resources/application-$profile.properties").inputStream().use(props::load) + if (File("${System.getenv("HOME")}/secrets").exists()) { + file("${System.getenv("HOME")}/secrets").inputStream().use(props::load) + } + val databaseUrl: String = props.getProperty("spring.datasource.url") - val secretsPath = System.getenv("DB_SECRETS_PATH") - if (secretsPath != null) { + System.getenv("DATABASE_SECRETS_PATH")?.let { secretsPath -> // Branch for environment with explicit file with database credentials, e.g. Kubernetes Secrets - val url = file("$secretsPath/spring.datasource.url").readText() val username = file("$secretsPath/spring.datasource.username").readText() val password = file("$secretsPath/spring.datasource.password").readText() - return DatabaseCredentials(url, username, password) - } else { - // Branch for other environments, e.g. local deployment or server deployment - file("save-backend/src/main/resources/application-$profile.properties").inputStream().use(props::load) - if (File("${System.getenv("HOME")}/secrets").exists()) { - file("${System.getenv("HOME")}/secrets").inputStream().use(props::load) - } + return DatabaseCredentials(databaseUrl, username, password) } - val databaseUrl: String val username: String val password: String if (profile == "prod") { - databaseUrl = props.getProperty("spring.datasource.url") username = props.getProperty("username") password = props.getProperty("password") } else { - databaseUrl = props.getProperty("datasource.dev.url") username = props.getProperty("spring.datasource.username") password = props.getProperty("spring.datasource.password") } diff --git a/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DockerStackConfiguration.kt b/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DockerStackConfiguration.kt index 1b5534c8fa..d6ee6c50b2 100644 --- a/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DockerStackConfiguration.kt +++ b/buildSrc/src/main/kotlin/com/saveourtool/save/buildutils/DockerStackConfiguration.kt @@ -53,7 +53,6 @@ fun Project.createStackDeployTask(profile: String) { | - "3306:3306" | environment: | - "MYSQL_ROOT_PASSWORD=123" - | - "MYSQL_DATABASE=save_cloud" | zookeeper: | image: confluentinc/cp-zookeeper:latest | environment: @@ -105,6 +104,7 @@ fun Project.createStackDeployTask(profile: String) { FRONTEND_TAG=${defaultVersionOrProperty("frontend.dockerTag")} GATEWAY_TAG=${defaultVersionOrProperty("gateway.dockerTag")} ORCHESTRATOR_TAG=${defaultVersionOrProperty("orchestrator.dockerTag")} + SANDBOX_TAG=${defaultVersionOrProperty("sandbox.dockerTag")} PREPROCESSOR_TAG=${defaultVersionOrProperty("preprocessor.dockerTag")} PROFILE=$profile """.trimIndent() @@ -140,6 +140,7 @@ fun Project.createStackDeployTask(profile: String) { Files.createDirectories(configsDir.resolve("backend")) Files.createDirectories(configsDir.resolve("gateway")) Files.createDirectories(configsDir.resolve("orchestrator")) + Files.createDirectories(configsDir.resolve("sandbox")) Files.createDirectories(configsDir.resolve("preprocessor")) } description = @@ -225,6 +226,7 @@ fun Project.createStackDeployTask(profile: String) { "up", "-d", "orchestrator", + "sandbox", "backend", "frontend", "preprocessor" @@ -243,7 +245,7 @@ fun Project.createStackDeployTask(profile: String) { project(componentName).tasks.named("bootBuildImage") dependsOn(buildTask) val serviceName = when (componentName) { - "save-backend", "save-frontend", "save-orchestrator", "save-preprocessor" -> "save_${componentName.substringAfter("save-")}" + "save-backend", "save-frontend", "save-orchestrator", "save-sandbox", "save-preprocessor" -> "save_${componentName.substringAfter("save-")}" "api-gateway" -> "save_gateway" else -> error("Wrong component name $componentName") } diff --git a/docker-compose.yaml b/docker-compose.yaml index ec83c8d181..e33a688917 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -33,16 +33,34 @@ services: labels: - "prometheus-job=save-orchestrator" logging: *loki-logging-jvm + sandbox: + image: ghcr.io/saveourtool/save-sandbox:${SANDBOX_TAG} + user: root # to access host's docker socket + environment: + - "SPRING_PROFILES_ACTIVE=${PROFILE},docker-secrets" + secrets: + - db_username + - db_password + ports: + - "5400:5400" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - /home/saveu/configs/sandbox:/home/cnb/config + - save-fs-storage:/home/cnb/files + - save-tmp-resources:/tmp + extra_hosts: + - "host.docker.internal:host-gateway" + deploy: + labels: + - "prometheus-job=save-sandbox" + logging: *loki-logging-jvm backend: image: ghcr.io/saveourtool/save-backend:${BACKEND_TAG} environment: - "SPRING_PROFILES_ACTIVE=${PROFILE},secure,docker-secrets" - - "MYSQL_USER=/run/secrets/db_username" - - "MYSQL_PASSWORD_FILE=/run/secrets/db_password" secrets: - db_username - db_password - - db_url volumes: - save-fs-storage:/home/cnb/files - /home/saveu/configs/backend:/home/cnb/config @@ -144,5 +162,3 @@ secrets: external: true db_username: external: true - db_url: - external: true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d3e3de3ff2..f7ffb780b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -84,6 +84,7 @@ spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-star spring-boot-starter-data-jpa = { module = "org.springframework.boot:spring-boot-starter-data-jpa" } spring-boot-starter-quartz = { module = "org.springframework.boot:spring-boot-starter-quartz" } spring-boot-starter-security = { module = "org.springframework.boot:spring-boot-starter-security" } +spring-boot = { module = "org.springframework.boot:spring-boot" } spring-boot-configuration-processor = { module = "org.springframework.boot:spring-boot-configuration-processor", version.ref = "spring-boot" } spring-security-core = { module = "org.springframework.security:spring-security-core" } spring-security-oauth2-client = { module = "org.springframework.security:spring-security-oauth2-client" } diff --git a/save-backend/src/main/resources/META-INF/spring.factories b/save-backend/src/main/resources/META-INF/spring.factories index de7dfa2be1..b1a3f50ce9 100644 --- a/save-backend/src/main/resources/META-INF/spring.factories +++ b/save-backend/src/main/resources/META-INF/spring.factories @@ -1 +1 @@ -org.springframework.boot.env.EnvironmentPostProcessor=com.saveourtool.save.backend.postprocessor.DockerSecretsDatabaseProcessor \ No newline at end of file +org.springframework.boot.env.EnvironmentPostProcessor=com.saveourtool.save.spring.postprocessor.DockerSecretsDatabaseProcessor \ No newline at end of file diff --git a/save-backend/src/main/resources/application-dev.properties b/save-backend/src/main/resources/application-dev.properties index 3f6e6d5fc6..d971ddc7ad 100644 --- a/save-backend/src/main/resources/application-dev.properties +++ b/save-backend/src/main/resources/application-dev.properties @@ -1,7 +1,4 @@ -spring.liquibase.enabled=false -# `mysql` is resolved when running mysql in docker-compose, change to `datasource.dev.url` if running backend via `bootRun` or from IDE spring.datasource.url=jdbc:mysql://localhost:3306/save_cloud -datasource.dev.url=jdbc:mysql://localhost:3306/save_cloud spring.datasource.username=root spring.datasource.password=123 backend.url=http://host.docker.internal:${server.port} diff --git a/save-backend/src/main/resources/application-prod.properties b/save-backend/src/main/resources/application-prod.properties index 027284d1d5..3108a89ee1 100644 --- a/save-backend/src/main/resources/application-prod.properties +++ b/save-backend/src/main/resources/application-prod.properties @@ -1,3 +1,3 @@ -spring.liquibase.enabled=false +spring.datasource.url=jdbc:mysql://192.168.0.250:3306/save_cloud spring.quartz.job-store-type=jdbc spring.quartz.jdbc.initialize-schema=ALWAYS diff --git a/save-backend/src/main/resources/application.properties b/save-backend/src/main/resources/application.properties index 2139a247e4..f20f6b485b 100644 --- a/save-backend/src/main/resources/application.properties +++ b/save-backend/src/main/resources/application.properties @@ -3,7 +3,7 @@ backend.preprocessorUrl=http://preprocessor:5200 backend.orchestratorUrl=http://orchestrator:5100 backend.initialBatchSize=100 backend.fileStorage.location=/home/cnb/files -server.port = 5800 +server.port=5800 server.error.path=/error management.endpoints.web.exposure.include=health,info,prometheus,quartz server.error.include-message=always @@ -20,3 +20,4 @@ spring.jpa.properties.hibernate.order_updates=true logging.level.org.hibernate.engine.internal.StatisticalLoggingSessionEventListener=WARN spring.cloud.kubernetes.enabled=false spring.codec.max-in-memory-size=100MB +spring.liquibase.enabled=false diff --git a/save-cloud-charts/save-cloud/README.md b/save-cloud-charts/save-cloud/README.md index f5abef0047..304abdcfab 100644 --- a/save-cloud-charts/save-cloud/README.md +++ b/save-cloud-charts/save-cloud/README.md @@ -6,8 +6,7 @@ api-gateway acts as an entrypoint and svc/gateway is actually a LoadBalancer. ## Prerequisites * save-backend expects the following secrets to be set under the secret `db-secrets` (`kubectl create secret generic db-secrets <...>`, - also see Secrets section in dev profile in [mysql-deployment.yaml](templates/mysql-deployment.yaml) as a reference): - * `spring.datasource.url` + also see Secrets section in dev profile in [mysql-deployment.yaml](templates/mysql-deployment.yaml) as a reference): * `spring.datasource.username` * `spring.datasource.password` diff --git a/save-cloud-charts/save-cloud/templates/backend-deployment.yaml b/save-cloud-charts/save-cloud/templates/backend-deployment.yaml index 571489358b..36d7d82718 100644 --- a/save-cloud-charts/save-cloud/templates/backend-deployment.yaml +++ b/save-cloud-charts/save-cloud/templates/backend-deployment.yaml @@ -26,7 +26,7 @@ spec: env: {{- include "spring-boot.common.env" (merge (dict "service" .Values.backend) .) | nindent 12 }} - name: DATABASE_SECRETS_PATH - value: {{ .Values.backend.dbPasswordFile }} + value: {{ .Values.mysql.dbPasswordFile }} - name: JAVA_TOOL_OPTIONS value: -XX:ReservedCodeCacheSize=48M volumeMounts: @@ -34,7 +34,7 @@ spec: - name: fs-storage mountPath: /home/cnb/files - name: database-secret - mountPath: {{ .Values.backend.dbPasswordFile }} + mountPath: {{ .Values.mysql.dbPasswordFile }} {{- include "spring-boot.management" .Values.backend | nindent 10 }} resources: limits: @@ -62,7 +62,7 @@ spec: runAsUser: 1001 runAsGroup: 1001 args: - - --url=$(DB_URL) + - --url=$(DB_URL)?createDatabaseIfNotExist=true - --changeLogFile=db/db.changelog-master.xml - --username=$(DB_USERNAME) - --password=$(DB_PASSWORD) @@ -82,7 +82,7 @@ spec: valueFrom: secretKeyRef: name: db-secrets - key: spring.datasource.url + key: spring.datasource.backend-url - name: DB_USERNAME valueFrom: secretKeyRef: @@ -96,7 +96,7 @@ spec: volumeMounts: - mountPath: /liquibase/changelog name: migrations-data - - mountPath: {{ .Values.backend.dbPasswordFile }} + - mountPath: {{ .Values.mysql.dbPasswordFile }} name: database-secret {{ end }} volumes: diff --git a/save-cloud-charts/save-cloud/templates/mysql-deployment.yaml b/save-cloud-charts/save-cloud/templates/mysql-deployment.yaml index 20a827c75f..b8e08808f8 100644 --- a/save-cloud-charts/save-cloud/templates/mysql-deployment.yaml +++ b/save-cloud-charts/save-cloud/templates/mysql-deployment.yaml @@ -6,9 +6,10 @@ kind: Secret metadata: name: db-secrets stringData: - spring.datasource.url: 'jdbc:mysql://mysql-service:3306/{{ .Values.mysql.schema }}' + spring.datasource.backend-url: 'jdbc:mysql://mysql-service:3306/{{ .Values.mysql.backend_schema }}' + spring.datasource.sandbox-url: 'jdbc:mysql://mysql-service:3306/{{ .Values.mysql.sandbox_schema }}' spring.datasource.username: root - spring.datasource.password: '123' + spring.datasource.password: {{ .Values.mysql.root_password }} --- {{ end }} @@ -36,9 +37,7 @@ spec: name: mysql env: - name: MYSQL_ROOT_PASSWORD - value: '123' - - name: MYSQL_DATABASE - value: save_cloud + value: {{ .Values.mysql.root_password }} volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql diff --git a/save-cloud-charts/save-cloud/templates/database-service.yaml b/save-cloud-charts/save-cloud/templates/mysql-service.yaml similarity index 100% rename from save-cloud-charts/save-cloud/templates/database-service.yaml rename to save-cloud-charts/save-cloud/templates/mysql-service.yaml diff --git a/save-cloud-charts/save-cloud/templates/sandbox-configmap.yaml b/save-cloud-charts/save-cloud/templates/sandbox-configmap.yaml new file mode 100644 index 0000000000..80c8f71eab --- /dev/null +++ b/save-cloud-charts/save-cloud/templates/sandbox-configmap.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.sandbox.name }}-config +data: + application.properties: | + sandbox.backendUrl=N/A + + sandbox.kubernetes.apiServerUrl=http://kubernetes.default.svc + sandbox.kubernetes.serviceAccount=${POD_SERVICE_ACCOUNT} + sandbox.kubernetes.namespace=${POD_NAMESPACE} + + server.shutdown=graceful + management.endpoints.web.exposure.include=* + sandbox.agent-settings.orchestrator-url=http://{{ .Values.sandbox.name }} + sandbox.agent-settings.backend-url=http://{{ .Values.sandbox.name }} + sandbox.agent-settings.debug=true + + sandbox.test-resources.tmp-path=/tmp/save/resources + + logging.level.com.saveourtool.save.orchestrator.kubernetes=DEBUG + + {{ if .Values.sandbox.applicationProperties }} + {{- .Values.sandbox.applicationProperties | nindent 4 }} + {{ end }} \ No newline at end of file diff --git a/save-cloud-charts/save-cloud/templates/sandbox-deployment.yaml b/save-cloud-charts/save-cloud/templates/sandbox-deployment.yaml new file mode 100644 index 0000000000..e2e0216269 --- /dev/null +++ b/save-cloud-charts/save-cloud/templates/sandbox-deployment.yaml @@ -0,0 +1,125 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sandbox + labels: + {{- include "common.labels" (merge (dict "service" .Values.sandbox) .) | nindent 4 }} +spec: + selector: + matchLabels: + io.kompose.service: sandbox + replicas: 1 + template: + metadata: + labels: + {{- include "pod.common.labels" (merge (dict "service" .Values.sandbox ) .) | nindent 8 }} + annotations: + {{- include "pod.common.annotations" (dict "service" .Values.backend ) | nindent 8 }} + spec: + serviceAccountName: sandbox-sa + restartPolicy: Always + {{- if .Values.sandbox.nodeName }} + nodeName: {{ .Values.sandbox.nodeName }} + {{- end }} + {{- include "cnb.securityContext" . | nindent 6 }} + containers: + - name: sandbox + {{- include "spring-boot.common" (merge (dict "service" .Values.sandbox) .) | nindent 10 }} + env: + {{- include "spring-boot.common.env" (merge (dict "service" .Values.sandbox) .) | nindent 12 }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: JAVA_TOOL_OPTIONS + value: -XX:ReservedCodeCacheSize=48M + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_SERVICE_ACCOUNT + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + volumeMounts: + - {{ include "spring-boot.config-volume-mount" . | indent 14 | trim }} + - name: fs-storage + mountPath: /home/cnb/files + - name: database-secret + mountPath: {{ .Values.mysql.dbPasswordFile }} + {{- include "spring-boot.management" .Values.sandbox | nindent 10 }} + resources: + limits: + memory: 800M + requests: + memory: 600M + initContainers: + - name: git-cloner + image: alpine/git + args: + - clone + - --single-branch + - --branch + - {{ .Values.mysql.migrations.branch | default "master" }} + - -- + - https://github.com/saveourtool/save-cloud.git + - /data + volumeMounts: + - mountPath: /data + name: migrations-data + - name: liquibase-runner + image: liquibase/liquibase:4.15 + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + args: + - --url=$(DB_URL)?createDatabaseIfNotExist=true + - --changeLogFile=save-sandbox/db/db.changelog-sandbox.xml + - --username=$(DB_USERNAME) + - --password=$(DB_PASSWORD) + - --log-level=info + - --contexts={{ .Values.profile }} + - update + resources: + requests: + memory: 100M + limits: + memory: 300M + env: + # See https://hub.docker.com/r/liquibase/liquibase, section 'Notice for MySQL Users' + - name: INSTALL_MYSQL + value: 'true' + - name: DB_URL + valueFrom: + secretKeyRef: + name: db-secrets + key: spring.datasource.sandbox-url + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: db-secrets + key: spring.datasource.username + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-secrets + key: spring.datasource.password + volumeMounts: + - mountPath: /liquibase/changelog + name: migrations-data + - mountPath: {{ .Values.mysql.dbPasswordFile }} + name: database-secret + volumes: + - {{ include "spring-boot.config-volume" (dict "service" .Values.sandbox) | indent 10 | trim }} + - name: fs-storage + persistentVolumeClaim: + claimName: save-fs-storage + - name: database-secret + secret: + secretName: db-secrets + - name: migrations-data + emptyDir: { } diff --git a/save-cloud-charts/save-cloud/templates/sandbox-service-account.yaml b/save-cloud-charts/save-cloud/templates/sandbox-service-account.yaml new file mode 100644 index 0000000000..1f44e4ba04 --- /dev/null +++ b/save-cloud-charts/save-cloud/templates/sandbox-service-account.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: sandbox-sa + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: jobs-executor +rules: + - apiGroups: [batch] + resources: [jobs] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: [""] # "" indicates the core API group + resources: [pods] + verbs: [list, get, delete, create] + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: sandbox-jobs-binding +subjects: + - kind: ServiceAccount + name: sandbox-sa +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: jobs-executor diff --git a/save-cloud-charts/save-cloud/templates/sandbox-service.yaml b/save-cloud-charts/save-cloud/templates/sandbox-service.yaml new file mode 100644 index 0000000000..a2cdb9e7f6 --- /dev/null +++ b/save-cloud-charts/save-cloud/templates/sandbox-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: +{{- include "service.common.metadata" (dict "service" .Values.sandbox) | nindent 2 }} +spec: + {{ if .Values.sandbox.clusterIP }} + clusterIP: {{ .Values.sandbox.clusterIP }} + {{ end }} + ports: + {{- include "service.common.ports" (dict "service" .Values.sandbox) | nindent 4 }} + selector: + {{- include "service.common.selectors" (dict "service" .Values.sandbox) | nindent 4 }} diff --git a/save-cloud-charts/save-cloud/values-minikube.yaml b/save-cloud-charts/save-cloud/values-minikube.yaml index 75f08660d9..8447aab24e 100644 --- a/save-cloud-charts/save-cloud/values-minikube.yaml +++ b/save-cloud-charts/save-cloud/values-minikube.yaml @@ -16,6 +16,13 @@ orchestrator: orchestrator.docker.host=tcp://localhost:2376 logging.level.com.saveourtool=DEBUG orchestrator.kubernetes.useGvisor=false +sandbox: + profile: dev,kubernetes,minikube + dockerHost: tcp://${HOST_IP}:2376 + applicationProperties: | + sandbox.docker.host=tcp://localhost:2376 + logging.level.com.saveourtool=DEBUG + sandbox.kubernetes.useGvisor=false mysql: external: false ip: nil diff --git a/save-cloud-charts/save-cloud/values.yaml b/save-cloud-charts/save-cloud/values.yaml index 2c49279c97..6a515917de 100644 --- a/save-cloud-charts/save-cloud/values.yaml +++ b/save-cloud-charts/save-cloud/values.yaml @@ -13,7 +13,6 @@ backend: # Fixed ClusterIP can be assigned to make it easier to query backend from services outside Kubernetes. # Should be chosen depending on cluster's network configuration: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address. clusterIP: null - dbPasswordFile: /home/cnb/secrets/db_secrets frontend: name: frontend imageName: save-frontend @@ -25,6 +24,13 @@ orchestrator: # Fixed ClusterIP can be assigned to make it easier to query orchestrator from services outside Kubernetes clusterIP: null dockerHost: tcp://${HOST_IP}:2375 +sandbox: + name: sandbox + imageName: save-sandbox + containerPort: 5400 + # Fixed ClusterIP can be assigned to make it easier to query orchestrator from services outside Kubernetes + clusterIP: null + dockerHost: tcp://${HOST_IP}:2375 preprocessor: name: preprocessor imageName: save-preprocessor @@ -57,10 +63,13 @@ mysql: # As an example, this is what may be a resolved IP of `host.minikube.internal`. ip: 192.168.65.2 # Name of the database schema that will be used by save-cloud deployment - schema: save_cloud + backend_schema: save_cloud + sandbox_schema: save_sandbox + root_password: '123' migrations: # Whether database migrations should be executed while deploying the application enabled: false + dbPasswordFile: /home/cnb/secrets/db_secrets # Values for dependencies grafana: diff --git a/save-cloud-common/build.gradle.kts b/save-cloud-common/build.gradle.kts index b304f6d50f..3039193cd0 100644 --- a/save-cloud-common/build.gradle.kts +++ b/save-cloud-common/build.gradle.kts @@ -45,6 +45,7 @@ kotlin { implementation(project.dependencies.platform(libs.spring.boot.dependencies)) implementation(libs.spring.security.core) implementation(libs.spring.web) + implementation(libs.spring.boot) implementation(libs.jackson.module.kotlin) implementation(libs.hibernate.jpa21.api) api(libs.slf4j.api) diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/postprocessor/DockerSecretsDatabaseProcessor.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/spring/postprocessor/DockerSecretsDatabaseProcessor.kt similarity index 85% rename from save-backend/src/main/kotlin/com/saveourtool/save/backend/postprocessor/DockerSecretsDatabaseProcessor.kt rename to save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/spring/postprocessor/DockerSecretsDatabaseProcessor.kt index e2d6ee55b2..18462f1da3 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/postprocessor/DockerSecretsDatabaseProcessor.kt +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/spring/postprocessor/DockerSecretsDatabaseProcessor.kt @@ -1,4 +1,4 @@ -package com.saveourtool.save.backend.postprocessor +package com.saveourtool.save.spring.postprocessor import org.springframework.boot.SpringApplication import org.springframework.boot.env.EnvironmentPostProcessor @@ -25,21 +25,18 @@ class DockerSecretsDatabaseProcessor( log.debug("Skipping activation of ${this::class.simpleName} because of active profiles") return } - val secretsBasePath = System.getenv("DB_PASSWORD_FILE") ?: "/run/secrets" + val secretsBasePath = System.getenv("SECRETS_PATH") ?: "/run/secrets" log.debug("Started DockerSecretsDatabaseProcessor [EnvironmentPostProcessor] configured to look up secrets in $secretsBasePath") val passwordResource = FileSystemResource("$secretsBasePath/db_password") val usernameResource = FileSystemResource("$secretsBasePath/db_username") - val jdbcUrlResource = FileSystemResource("$secretsBasePath/db_url") if (passwordResource.exists()) { log.debug("Acquired password. Beginning to setting properties") val dbPassword = passwordResource.inputStream.use { StreamUtils.copyToString(it, Charset.defaultCharset()) } val dbUsername = usernameResource.inputStream.use { StreamUtils.copyToString(it, Charset.defaultCharset()) } - val dbUrl = jdbcUrlResource.inputStream.use { StreamUtils.copyToString(it, Charset.defaultCharset()) } val props = Properties() props["spring.datasource.password"] = dbPassword props["spring.datasource.username"] = dbUsername - props["spring.datasource.url"] = dbUrl environment.propertySources.addLast(PropertiesPropertySource("dbProps", props)) log.debug("Properties have been set") } diff --git a/save-sandbox/db/db.changelog-sandbox.xml b/save-sandbox/db/db.changelog-sandbox.xml new file mode 100644 index 0000000000..5162d1a17b --- /dev/null +++ b/save-sandbox/db/db.changelog-sandbox.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/save-sandbox/src/main/resources/META-INF/spring.factories b/save-sandbox/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..b1a3f50ce9 --- /dev/null +++ b/save-sandbox/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.env.EnvironmentPostProcessor=com.saveourtool.save.spring.postprocessor.DockerSecretsDatabaseProcessor \ No newline at end of file diff --git a/save-sandbox/src/main/resources/application-dev.properties b/save-sandbox/src/main/resources/application-dev.properties index 6ad8618c88..72e0de862a 100644 --- a/save-sandbox/src/main/resources/application-dev.properties +++ b/save-sandbox/src/main/resources/application-dev.properties @@ -1,4 +1,7 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/save_sandbox +spring.datasource.username=root +spring.datasource.password=123 sandbox.backendUrl=http://localhost:5800/internal sandbox.docker.logging-driver=json sandbox.docker.test-resources-volume-name=save-tmp-resources -sandbox.docker.test-resources-volume-type=bind \ No newline at end of file +sandbox.docker.test-resources-volume-type=bind diff --git a/save-sandbox/src/main/resources/application-prod.properties b/save-sandbox/src/main/resources/application-prod.properties new file mode 100644 index 0000000000..6bfdd3736a --- /dev/null +++ b/save-sandbox/src/main/resources/application-prod.properties @@ -0,0 +1 @@ +spring.datasource.url=jdbc:mysql://192.168.0.250:3306/save_sandbox diff --git a/save-sandbox/src/main/resources/application.properties b/save-sandbox/src/main/resources/application.properties index 513efde405..fd9d303ae4 100644 --- a/save-sandbox/src/main/resources/application.properties +++ b/save-sandbox/src/main/resources/application.properties @@ -21,3 +21,4 @@ sandbox.agent-settings.orchestrator-url=http://host.docker.internal:${server.por sandbox.agent-settings.debug=true sandbox.agents-start-timeout-millis=60000 sandbox.agents-start-check-interval-millis=10000 +spring.liquibase.enabled=false