All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intershop.gradle.icm.docker.tasks.AbstractICMASContainerTask.kt Maven / Gradle / Ivy

There is a newer version: 5.3.1
Show newest version
/*
 * Copyright 2020 Intershop Communications AG.
 *
 * 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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.intershop.gradle.icm.docker.tasks

import com.github.dockerjava.api.async.ResultCallback
import com.github.dockerjava.api.async.ResultCallbackTemplate
import com.github.dockerjava.api.command.ExecCreateCmdResponse
import com.github.dockerjava.api.model.Frame
import com.intershop.gradle.icm.docker.extension.DevelopmentConfiguration.ASPortConfiguration
import com.intershop.gradle.icm.docker.extension.DevelopmentConfiguration.DatabaseParameters
import com.intershop.gradle.icm.docker.extension.DevelopmentConfiguration.WebserverConfiguration
import com.intershop.gradle.icm.docker.extension.IntershopDockerExtension
import com.intershop.gradle.icm.docker.tasks.utils.AdditionalICMParameters
import com.intershop.gradle.icm.docker.tasks.utils.ContainerEnvironment
import com.intershop.gradle.icm.docker.tasks.utils.ICMContainerEnvironmentBuilder
import com.intershop.gradle.icm.docker.tasks.utils.ICMContainerEnvironmentBuilder.ClasspathLayout
import com.intershop.gradle.icm.docker.utils.Configuration
import com.intershop.gradle.icm.utils.JavaDebugSupport
import com.intershop.gradle.icm.utils.JavaDebugSupport.Companion.TASK_OPTION_VALUE_FALSE
import com.intershop.gradle.icm.utils.JavaDebugSupport.Companion.TASK_OPTION_VALUE_NO
import com.intershop.gradle.icm.utils.JavaDebugSupport.Companion.TASK_OPTION_VALUE_SUSPEND
import com.intershop.gradle.icm.utils.JavaDebugSupport.Companion.TASK_OPTION_VALUE_TRUE
import com.intershop.gradle.icm.utils.JavaDebugSupport.Companion.TASK_OPTION_VALUE_YES
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.options.Option
import org.gradle.api.tasks.options.OptionValues
import org.gradle.kotlin.dsl.getByType
import javax.inject.Inject

/**
 * Abstract base task to run a typical ICM-AS classes on a previously prepared container
 * @see com.intershop.gradle.icm.docker.utils.appserver.ContainerTaskPreparer
 * @param  result callback type
 * @param  result callback template type
 * @param  execution result type
 */
abstract class AbstractICMASContainerTask, RCT : ResultCallbackTemplate, ER>
@Inject constructor(project: Project) : AbstractContainerTask() {

    companion object {
        const val DEFAULT_COMMAND = "/intershop/bin/intershop.sh"
    }

    private val debugProperty: Property = project.objects.property(JavaDebugSupport::class.java)

    /**
     * The database configuration. It is lazily determined from
     * [com.intershop.gradle.icm.docker.extension.DevelopmentConfiguration.databaseConfiguration]
     */
    @get:Input
    val databaseConfiguration: Property by lazy {
        project.objects.property(DatabaseParameters::class.java)
                .value(project.extensions.getByType().developmentConfig.databaseConfiguration)
    }

    /**
     * The webserver configuration. It is lazily determined from
     * [com.intershop.gradle.icm.docker.extension.DevelopmentConfiguration.webserverConfiguration]
     */
    @get:Input
    val webserverConfiguration: Property by lazy {
        project.objects.property(WebserverConfiguration::class.java)
            .value(project.extensions.getByType().developmentConfig.webserverConfiguration)
    }

    /**
     * The port configuration. It is lazily determined from
     * [com.intershop.gradle.icm.docker.extension.DevelopmentConfiguration.asPortConfiguration]
     */
    @get:Input
    val portConfiguration: Property by lazy {
        project.objects.property(ASPortConfiguration::class.java)
                .value(project.extensions.getByType().developmentConfig.asPortConfiguration)
    }

    /**
     * The cartridge list to be used to start the ICM-AS server
     */
    @get: Input
    val cartridgeList: SetProperty by lazy {
        val cartListProvider = project.extensions.getByType().developmentConfig.cartridgeList
        if (cartListProvider.get().isEmpty()) {
            throw GradleException("Build property intershop_docker.developmentConfig.cartridgeList denotes an empty " +
                                  "set. Please provide a non-empty set.")
        }
        cartListProvider
    }

    /**
     * The cartridge list to be used to start the ICM-AS server for tests
     */
    @get: Input
    val testCartridgeList: SetProperty by lazy {
        val cartListProvider =
                project.extensions.getByType().developmentConfig.testCartridgeList
        cartListProvider
    }

    /**
     * Enable debugging for the JVM running the ICM-AS inside the container. This option defaults to the value
     * of the JVM property (SYSPROP_DEBUG_JVM) respectively `false` if not set.
     * The port on the host can be configured using the property `icm.properties/intershop.as.debug.port`
     *
     * @see com.intershop.gradle.icm.docker.utils.Configuration.AS_DEBUG_PORT
     */
    @set:Option(
            option = "debug-icm",
            description = "Enable/control debugging for the process. The following values are supported: " +
                          "$TASK_OPTION_VALUE_TRUE/$TASK_OPTION_VALUE_YES - " +
                          "enable debugging, $TASK_OPTION_VALUE_SUSPEND - enable debugging in " +
                          "suspend-mode, every other value - disable debugging. The debugging port is controlled by " +
                          "icm-property '${Configuration.AS_DEBUG_PORT}'."
    )
    @get:Input
    var debug: String
        get() = debugProperty.get().renderTaskOptionValue()
        set(value) = debugProperty.set(JavaDebugSupport.parse(project, value))

    /**
     * Return the possible values for the task option [debug]
     */
    @OptionValues("debug-icm")
    fun getDebugOptionValues(): Collection = listOf(TASK_OPTION_VALUE_TRUE, TASK_OPTION_VALUE_YES,
            TASK_OPTION_VALUE_SUSPEND, TASK_OPTION_VALUE_FALSE, TASK_OPTION_VALUE_NO)

    init {
        debugProperty.convention(JavaDebugSupport.defaults(project))

        group = "icm docker project"
    }

    /**
     * Executes a `docker-exec` on the container provided by [containerId]. This `docker-exec` uses the environment
     * variables provided by [createContainerEnvironment] and the callback created by [createCallback].
     * When the `docker-exec` has finished the return code is processed by [processExecutionResult]. Finally
     * [postRunRemoteCommand] is executed.
     */
    override fun runRemoteCommand() {
        val callback = createCallback()

        val execCmd = dockerClient.execCreateCmd(containerId.get())
        execCmd.withAttachStderr(true)
        execCmd.withAttachStdout(true)
        val command = getCommand()
        execCmd.withCmd(*command.toTypedArray())

        val env = createContainerEnvironment()
        execCmd.withEnv(env.toList())

        project.logger.quiet("Attempting to execute command '{}' on container {} using {}", command, containerId.get(),
                env)

        val execResponse: ExecCreateCmdResponse = execCmd.exec()

        val execCallback = dockerClient.execStartCmd(execResponse.id).withDetach(false).exec(callback)

        val exitCode = waitForCompletion(execCallback, execResponse)

        processExecutionResult(exitCode)

        postRunRemoteCommand(execCallback)
    }

    /**
     * Returns the command to be executed inside the container
     */
    @Internal
    protected open fun getCommand(): List = listOf("/bin/sh", "-c", DEFAULT_COMMAND)

    /**
     * Processes the exit code of the command executed inside the container. This function is executed right after
     * the command executed inside the container has finished.
     * Subclasses may overwrite this method to do same custom stuff.
     */
    protected open fun processExecutionResult(executionResult: ER) {
        project.logger.quiet("Command execution inside the container finished with execution result: {}",
                executionResult)
    }

    /**
     * This function (actually does nothing) is executed right after [processExecutionResult].
     * Subclasses may overwrite this method to do same custom stuff.
     */
    protected open fun postRunRemoteCommand(resultCallbackTemplate: RCT) = Unit

    /**
     * This function creates the environment used for the docker-exec.
     * Subclasses may overwrite this method to add some extract environment variables (keep super-variables).
     */
    protected open fun createContainerEnvironment(): ContainerEnvironment {
        return ICMContainerEnvironmentBuilder()
                .withContainerName(containerName)
                .withDatabaseConfig(databaseConfiguration.get())
                .withWebserverConfig(webserverConfiguration.get())
                .withPortConfig(portConfiguration.get())
                .withCartridgeList(createCartridgeList().get())
                .withAdditionalParameters(createAdditionalParameters())
                .withDebugOptions(debugProperty.get())
                // ensure release (product cartridges) and source (customization cartridges) layouts are recognized
                .withClasspathLayout(setOf(ClasspathLayout.RELEASE, ClasspathLayout.SOURCE))
                .build()
    }

    protected abstract fun waitForCompletion(resultCallbackTemplate: RCT, execResponse: ExecCreateCmdResponse): ER

    /**
     * Creates the list of cartridges to be used for the ICM-AS.
     */
    protected open fun createCartridgeList(): Provider> = cartridgeList

    /**
     * This function creates the additional parameters used for the environment variables (ENV_ADDITIONAL_PARAMETERS).
     * Subclasses may overwrite this method to add some extract parameters (keep super-parameters).
     * see intershop.sh
     */
    protected open fun createAdditionalParameters(): AdditionalICMParameters = AdditionalICMParameters()

    /**
     * Creates the [ResultCallbackTemplate] to be used for the docker-exec.
     */
    protected abstract fun createCallback(): RCT

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy