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

org.hidetake.gradle.ssh.internal.DefaultOperationHandler.groovy Maven / Gradle / Ivy

package org.hidetake.gradle.ssh.internal

import com.jcraft.jsch.ChannelExec
import com.jcraft.jsch.ChannelSftp
import com.jcraft.jsch.ChannelShell
import com.jcraft.jsch.Session
import groovy.transform.TupleConstructor
import groovy.util.logging.Slf4j
import org.codehaus.groovy.tools.Utilities
import org.hidetake.gradle.ssh.api.*
import org.hidetake.gradle.ssh.api.operation.ExecutionSettings
import org.hidetake.gradle.ssh.api.operation.ShellSettings
import org.hidetake.gradle.ssh.internal.session.ChannelManager

/**
 * Default implementation of {@link OperationHandler}.
 *
 * @author hidetake.org
 *
 */
@TupleConstructor
@Slf4j
class DefaultOperationHandler extends AbstractOperationHandler {
    final SshSpec sshSpec
    final SessionSpec sessionSpec
    final Session session
    final ChannelManager globalChannelManager

    @Override
    Remote getRemote() {
        sessionSpec.remote
    }

    @Override
    void shell(Map options, Closure interactions) {
        log.info("Execute a shell with options ($options)")

        def settings = new ShellSettings(logging: options.logging)
        def channelManager = new ChannelManager()
        try {
            def channel = session.openChannel('shell') as ChannelShell

            // TODO: removed in v0.3.0
            def remainingOptions = options.findAll { !(it.key in ['logging']) }
            remainingOptions.each { k, v ->
                channel[k] = v
                log.warn("Deprecated: JSch option `$k` will be no longer supported in v0.3.0")
            }

            def context = DefaultShellContext.create(channel, sshSpec.encoding)
            if (settings.logging) {
                context.enableLogging(sshSpec.outputLogLevel)
            }

            interactions.delegate = context
            interactions.resolveStrategy = Closure.DELEGATE_FIRST
            interactions()

            channel.connect()
            channelManager.add(channel)
            log.info("Channel #${channel.id} has been opened")
            channelManager.waitForPending()
            log.info("Channel #${channel.id} has been closed with exit status ${channel.exitStatus}")
            channelManager.validateExitStatus()
        } finally {
            channelManager.disconnect()
        }
    }

    @Override
    String execute(Map options, String command, Closure interactions) {
        log.info("Execute a command (${command}) with options ($options)")

        def settings = new ExecutionSettings(pty: options.pty, logging: options.logging)
        def channelManager = new ChannelManager()
        try {
            def channel = session.openChannel('exec') as ChannelExec
            channel.command = command
            channel.pty = settings.pty

            // TODO: removed in v0.3.0
            def remainingOptions = options.findAll { !(it.key in ['pty', 'logging']) }
            remainingOptions.each { k, v ->
                channel[k] = v
                log.warn("Deprecated: JSch option `$k` will be no longer supported in v0.3.0")
            }

            def context = DefaultCommandContext.create(channel, sshSpec.encoding)
            if (settings.logging) {
                context.enableLogging(sshSpec.outputLogLevel, sshSpec.errorLogLevel)
            }

            def lines = [] as List
            context.standardOutput.lineListeners.add { String line -> lines << line }

            interactions.delegate = context
            interactions.resolveStrategy = Closure.DELEGATE_FIRST
            interactions()

            channel.connect()
            channelManager.add(channel)
            log.info("Channel #${channel.id} has been opened")
            channelManager.waitForPending()
            log.info("Channel #${channel.id} has been closed with exit status ${channel.exitStatus}")
            channelManager.validateExitStatus()
            lines.join(Utilities.eol())
        } finally {
            channelManager.disconnect()
        }
    }

    @Override
    String executeSudo(Map options, String command) {
        log.info("Execute a command ($command) with sudo support and options ($options)")

        def prompt = UUID.randomUUID().toString()
        def lines = [] as List
        execute(options, "sudo -S -p '$prompt' $command") {
            interaction {
                when(partial: prompt, from: standardOutput) {
                    log.info("Sending password for sudo authentication on channel #${channel.id}")
                    standardInput << sessionSpec.remote.password << '\n'

                    when(nextLine: _, from: standardOutput) {
                        when(nextLine: 'Sorry, try again.') {
                            throw new RuntimeException("Sudo authentication failed on channel #${channel.id}")
                        }
                        when(line: _, from: standardOutput) {
                            lines << it
                        }
                    }
                }
                when(line: _, from: standardOutput) {
                    lines << it
                }
            }
        }

        lines.join(Utilities.eol())
    }

    @Override
    CommandContext executeBackground(Map options, String command) {
        log.info("Execute a command ($command) in background")

        def settings = new ExecutionSettings(pty: options.pty, logging: options.logging)
        def channel = session.openChannel('exec') as ChannelExec
        channel.command = command
        channel.pty = settings.pty

        // TODO: removed in v0.3.0
        def remainingOptions = options.findAll { !(it.key in ['pty', 'logging']) }
        remainingOptions.each { k, v ->
            channel[k] = v
            log.warn("Deprecated: JSch option `$k` will be no longer supported in v0.3.0")
        }

        def context = DefaultCommandContext.create(channel, sshSpec.encoding)
        if (settings.logging) {
            context.enableLogging(sshSpec.outputLogLevel, sshSpec.errorLogLevel)
        }

        globalChannelManager.add(channel)
        channel.connect()
        log.info("Channel #${channel.id} has been opened")

        context
    }

    @Override
    void get(Map options, String remote, String local) {
        log.info("Get a remote file (${remote}) to local (${local})")
        def channel = session.openChannel('sftp') as ChannelSftp

        // TODO: removed in v0.3.0
        options.each { k, v ->
            channel[k] = v
            log.warn("Deprecated: JSch option `$k` will be no longer supported in v0.3.0")
        }

        try {
            channel.connect()
            log.info("Channel #${channel.id} has been opened")
            channel.get(remote, local)
            log.info("Channel #${channel.id} has been closed with exit status ${channel.exitStatus}")
        } finally {
            channel.disconnect()
        }
    }

    @Override
    void put(Map options, String local, String remote) {
        log.info("Put a local file (${local}) to remote (${remote})")
        def channel = session.openChannel('sftp') as ChannelSftp

        // TODO: removed in v0.3.0
        options.each { k, v ->
            channel[k] = v
            log.warn("Deprecated: JSch option `$k` will be no longer supported in v0.3.0")
        }

        try {
            channel.connect()
            log.info("Channel #${channel.id} has been opened")
            channel.put(local, remote, ChannelSftp.OVERWRITE)
            log.info("Channel #${channel.id} has been closed with exit status ${channel.exitStatus}")
        } finally {
            channel.disconnect()
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy