/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * 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 org.guvnor.ala.docker.executor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import javax.inject.Inject; import com.spotify.docker.client.DockerException; import com.spotify.docker.client.messages.ContainerConfig; import com.spotify.docker.client.messages.ContainerCreation; import com.spotify.docker.client.messages.ContainerInfo; import com.spotify.docker.client.messages.HostConfig; import com.spotify.docker.client.messages.PortBinding; import java.util.Date; import org.guvnor.ala.config.Config; import org.guvnor.ala.config.RuntimeConfig; import org.guvnor.ala.docker.access.DockerAccessInterface; import org.guvnor.ala.docker.config.DockerRuntimeExecConfig; import org.guvnor.ala.docker.model.DockerProvider; import org.guvnor.ala.docker.model.DockerRuntime; import org.guvnor.ala.exceptions.ProvisioningException; import org.guvnor.ala.pipeline.FunctionConfigExecutor; import org.guvnor.ala.registry.RuntimeRegistry; import org.guvnor.ala.runtime.RuntimeBuilder; import org.guvnor.ala.runtime.RuntimeDestroyer; import org.guvnor.ala.runtime.RuntimeId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.guvnor.ala.docker.config.DockerRuntimeConfig; import org.guvnor.ala.docker.model.DockerRuntimeEndpoint; import org.guvnor.ala.docker.model.DockerRuntimeInfo; import org.guvnor.ala.docker.model.DockerRuntimeState; public class DockerRuntimeExecExecutor<T extends DockerRuntimeConfig> implements RuntimeBuilder<T, DockerRuntime>, RuntimeDestroyer, FunctionConfigExecutor<T, DockerRuntime> { private final RuntimeRegistry runtimeRegistry; private final DockerAccessInterface docker; protected static final Logger LOG = LoggerFactory.getLogger( DockerRuntimeExecExecutor.class ); @Inject public DockerRuntimeExecExecutor( final RuntimeRegistry runtimeRegistry, final DockerAccessInterface docker ) { this.runtimeRegistry = runtimeRegistry; this.docker = docker; } @Override public Optional<DockerRuntime> apply( final DockerRuntimeConfig config ) { final Optional<DockerRuntime> runtime = create( config ); if ( runtime.isPresent() ) { runtimeRegistry.registerRuntime( runtime.get() ); } return runtime; } private Optional<DockerRuntime> create( final DockerRuntimeConfig runtimeConfig ) throws ProvisioningException { if ( runtimeConfig.isPull() ) { try { LOG.info( "Pulling Docker Image: " + runtimeConfig.getImage() ); docker.getDockerClient( runtimeConfig.getProviderId() ).pull( runtimeConfig.getImage() ); } catch ( DockerException | InterruptedException ex ) { LOG.error( ex.getMessage(), ex ); throw new ProvisioningException( "Error Pulling Docker Image: " + runtimeConfig.getImage() + "with error: " + ex.getMessage() ); } } final String[] ports = { runtimeConfig.getPort() }; final Map<String, List<PortBinding>> portBindings = new HashMap<>(); final Optional<DockerProvider> _dockerProvider = runtimeRegistry.getProvider( runtimeConfig.getProviderId(), DockerProvider.class ); if ( !_dockerProvider.isPresent() ) { return Optional.empty(); } final DockerProvider dockerProvider = _dockerProvider.get(); final List<PortBinding> randomPort = new ArrayList<>(); final PortBinding randomPortBinding = PortBinding.randomPort( dockerProvider.getHostId() ); randomPort.add( randomPortBinding ); portBindings.put( runtimeConfig.getPort(), randomPort ); final HostConfig hostConfig = HostConfig.builder().portBindings( portBindings ).build(); final ContainerConfig containerConfig = ContainerConfig.builder() .hostConfig( hostConfig ) .image( runtimeConfig.getImage() ) .exposedPorts( ports ) .build(); final ContainerCreation creation; try { creation = docker.getDockerClient( runtimeConfig.getProviderId() ).createContainer( containerConfig ); docker.getDockerClient( runtimeConfig.getProviderId() ).startContainer( creation.id() ); } catch ( DockerException | InterruptedException ex ) { LOG.error( ex.getMessage(), ex ); throw new ProvisioningException( "Error Creating Docker Container with image: " + runtimeConfig.getImage() + "with error: " + ex.getMessage(), ex ); } final String id = creation.id(); String shortId = id.substring( 0, 12 ); String host = ""; ContainerInfo containerInfo; try { containerInfo = docker.getDockerClient( runtimeConfig.getProviderId() ).inspectContainer( id ); host = docker.getDockerClient( runtimeConfig.getProviderId() ).getHost(); } catch ( DockerException | InterruptedException ex ) { throw new ProvisioningException( "Error Getting Docker Container info: " + id + "with error: " + ex.getMessage(), ex ); } DockerRuntimeEndpoint dockerRuntimeEndpoint = new DockerRuntimeEndpoint(); dockerRuntimeEndpoint.setHost( host ); dockerRuntimeEndpoint.setPort( Integer.valueOf( runtimeConfig.getPort() ) ); dockerRuntimeEndpoint.setContext( "" ); return Optional.of( new DockerRuntime( shortId, runtimeConfig, dockerProvider, dockerRuntimeEndpoint, new DockerRuntimeInfo(), new DockerRuntimeState("Running", new Date().toString()) ) ); } @Override public Class<? extends Config> executeFor() { return DockerRuntimeExecConfig.class; } @Override public String outputId() { return "docker-runtime"; } @Override public boolean supports( final RuntimeConfig config ) { return config instanceof DockerRuntimeConfig; } @Override public boolean supports( final RuntimeId runtimeId ) { return runtimeId instanceof DockerRuntime || runtimeRegistry.getRuntimeById( runtimeId.getId() ) instanceof DockerRuntime; } @Override public void destroy( final RuntimeId runtimeId ) { try { LOG.info( "Killing Container: " + runtimeId.getId() ); docker.getDockerClient( runtimeId.getProviderId() ).killContainer( runtimeId.getId() ); LOG.info( "Removing Container: " + runtimeId.getId() ); docker.getDockerClient( runtimeId.getProviderId() ).removeContainer( runtimeId.getId() ); runtimeRegistry.unregisterRuntime( runtimeId ); } catch ( DockerException | InterruptedException ex ) { LOG.debug( ex.getMessage(), ex ); try { // Trying to remove the container if it cannot be killed LOG.info( "Attempting to Remove Container without Killing: " + runtimeId.getId() ); docker.getDockerClient( runtimeId.getProviderId() ).removeContainer( runtimeId.getId() ); runtimeRegistry.unregisterRuntime( runtimeId ); } catch ( DockerException | InterruptedException ex2 ) { LOG.error( ex.getMessage(), ex2 ); throw new ProvisioningException( "Error destroying Docker Runtime: " + ex.getMessage(), ex2 ); } } } }