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

com.exasol.containers.ssh.DockerAccess Maven / Gradle / Ivy

package com.exasol.containers.ssh;

import java.io.File;
import java.nio.file.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.Container.ExecResult;

import com.exasol.containers.ExasolContainerConstants;
import com.exasol.errorreporting.ExaError;
import com.jcraft.jsch.Session;

/**
 * 
    *
  • Store SSH public/private key pair *
  • Detect mode for connecting to the docker container and tell if the container supports Docker Exec. *
  • enable to create and instance of {@link Ssh} in order to connect to docker container via SSH. *
*/ public final class DockerAccess { private static final Logger LOGGER = LoggerFactory.getLogger(DockerAccess.class); private final SessionBuilderProvider sessionBuilderProvider; private final DockerProbe dockerProbe; private final SshKeys sshKeys; private final Path temporaryCredentialsDirectory; // The access mode is determined lazily and cached for later use. private Mode cachedAccessMode = Mode.UNKNOWN; // The SSH connection is created lazily and cached for later use. private Ssh cachedSshConnection = null; enum Mode { UNKNOWN, SSH, DOCKER_EXEC } private DockerAccess(final Builder builder) { this.sessionBuilderProvider = builder.sessionBuilderProvider; this.dockerProbe = builder.dockerProbe; this.sshKeys = builder.sshKeys; this.temporaryCredentialsDirectory = builder.temporaryCredentialsDirectory; } /** * Get the SSH key pair. * * @return SSH public / private key pair */ public SshKeys getSshKeys() { return this.sshKeys; } /** * Check whether we can use {@code docker exec} to access the docker container. * * @return {@code true} if docker container supports docker exec */ // [impl->dsn~detect-if-docker-exec-is-possible~1] public boolean supportsDockerExec() { return getMode() == Mode.DOCKER_EXEC; } /** * Get the access variant. *

* Note that the mode is determined upon the first call of this function and then cached to improve the performance * of later uses. *

* * @return either {@link Mode#DOCKER_EXEC} or {@link Mode#SSH}. */ synchronized Mode getMode() { if (this.cachedAccessMode == Mode.UNKNOWN) { final ExecResult result = this.dockerProbe.probeFile(ExasolContainerConstants.CLUSTER_CONFIGURATION_PATH); if (result.getExitCode() == 0) { LOGGER.trace("Docker container supports docker exec"); this.cachedAccessMode = Mode.DOCKER_EXEC; } else { LOGGER.trace("Docker container requires SSH access"); this.cachedAccessMode = Mode.SSH; } } return this.cachedAccessMode; } /** * Get an SSH connection. *

* Note that the SSH connection is created upon the first call of this function and then cached to improve the * performance of later uses. *

* * @return instance of {@link Ssh} providing SSH functionality */ // [impl->dsn~access-via-ssh~1] public synchronized Ssh getSsh() { if (this.cachedSshConnection == null) { createTemporaryCredentialDirectoryIfMissing(); final Session session = this.sessionBuilderProvider.get() // .identity(this.sshKeys.getIdentityProvider()) // .build(); this.cachedSshConnection = new Ssh(session); } return this.cachedSshConnection; } // [impl->dsn~auto-create-directory-for-temporary-credentials~1] private void createTemporaryCredentialDirectoryIfMissing() { final File directory = this.temporaryCredentialsDirectory.toFile(); if (!directory.exists() && !directory.mkdirs()) { ExaError.messageBuilder("F-ETC-23") // .message("Unable to create directory for temporary credentials: {{path}}") // .parameter("path", directory.toString()); } } /** * Builds a session with parameters host, port, and user in order to create an SSH connection. */ @FunctionalInterface public interface SessionBuilderProvider { /** * Create a new instance of {@link SessionBuilder} to be used by {@link DockerAccess} to build a new session and * connect to the Docker container. * * @return instance of {@link SessionBuilder} * @throws SshException in case SessionBuilder creation fails */ SessionBuilder get() throws SshException; } /** * Used to probe a specific file inside docker container in order to find out which {@link Mode} may be used to * connect to docker container. */ @FunctionalInterface public interface DockerProbe { /** * @param path path to be checked for existence * @return result of probing */ ExecResult probeFile(String path); } /** * @return a builder for a new instance of {@link DockerAccess}. */ public static Builder builder() { return new Builder(); } /** * Builder for a new instance of {@link DockerAccess}. */ public static final class Builder { private SessionBuilderProvider sessionBuilderProvider; private DockerProbe dockerProbe; private SshKeys sshKeys; private Path temporaryCredentialsDirectory; private Builder() { // use static method builder() } /** * Set the {@link SessionBuilderProvider}. * * @param sessionBuilderProvider provide an instance of {@link SessionBuilder} to build a session to instantiate * {@link Ssh} in method {@link DockerAccess#getSsh()}. * @return this for fluent programming */ public Builder sessionBuilderProvider(final SessionBuilderProvider sessionBuilderProvider) { this.sessionBuilderProvider = sessionBuilderProvider; return this; } /** * Set the docker probe. *

* Checks the existence of the cluster configuration file to determine if {@code docker exec} can be used to * access the docker container. *

* * @param dockerProbe {@link DockerProbe} used to check for existence of file * {@link ExasolContainerConstants#CLUSTER_CONFIGURATION_PATH}. * @return this for fluent programming */ public Builder dockerProbe(final DockerProbe dockerProbe) { this.dockerProbe = dockerProbe; return this; } /** * Set the temporary SSH keys. * * @param sshKeys pair of public and private SSH key to use for SSH connection * @return this for fluent programming */ public Builder sshKeys(final SshKeys sshKeys) { this.sshKeys = sshKeys; return this; } /** * Create a new instance of the docker access. * * @return new instance of {@link DockerAccess} */ public DockerAccess build() { return new DockerAccess(this); } /** * Set the directory in which temporary credentials are stored. * * @param temporaryCredentialsDirectory directory in which to store temporary credentials * @return this for fluent programming */ public Builder temporaryCredentialsDirectory(final Path temporaryCredentialsDirectory) { this.temporaryCredentialsDirectory = temporaryCredentialsDirectory; return this; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy