/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.traefik; import com.google.common.collect.ImmutableSet; import com.google.inject.name.Named; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.plugin.docker.client.DockerConnector; import org.eclipse.che.plugin.docker.client.json.ContainerConfig; import org.eclipse.che.plugin.docker.client.json.ImageInfo; import org.eclipse.che.plugin.docker.client.params.CreateContainerParams; import org.eclipse.che.plugin.docker.client.params.InspectImageParams; import org.eclipse.che.plugin.docker.machine.CustomServerEvaluationStrategy; import org.eclipse.che.plugin.docker.machine.ServerEvaluationStrategy; import org.eclipse.che.plugin.docker.machine.ServerEvaluationStrategyProvider; import javax.inject.Inject; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import static java.lang.String.format; /** * Traefik has a listener on Docker containers. Each time a container is added or removed, it checks if the container has specific Traefik * Labels and then create routes based upon these labels. * The job of this interceptor is to add Traefik labels prior the start of the container (it will be done when we create the container) by * adding Traefik labels. * The routes are built using the custom strategy template. * * @author Florent Benoit */ public class TraefikCreateContainerInterceptor implements MethodInterceptor { /** * Inject the server evaluation strategy provider. */ private ServerEvaluationStrategyProvider serverEvaluationStrategyProvider; /** * Template. */ private String template; /** * Grab labels of the config and from image to get all exposed ports and the labels defined if any * * @param methodInvocation * intercepting data of createContainer method on {@link DockerConnector} * @return the result of the intercepted method * @throws Throwable * if there is an exception */ @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { ServerEvaluationStrategy serverEvaluationStrategy = serverEvaluationStrategyProvider.get(); // Abort if custom server evaluation strategy is not enabled. if (!(CustomServerEvaluationStrategy.class.isInstance(serverEvaluationStrategy))) { return methodInvocation.proceed(); } final CustomServerEvaluationStrategy customServerEvaluationStrategy = (CustomServerEvaluationStrategy)serverEvaluationStrategy; // Get the connector DockerConnector dockerConnector = (DockerConnector)methodInvocation.getThis(); // only one parameter which is CreateContainerParams CreateContainerParams createContainerParams = (CreateContainerParams)methodInvocation.getArguments()[0]; // Grab container configuration ContainerConfig containerConfig = createContainerParams.getContainerConfig(); String image = containerConfig.getImage(); // first, get labels defined in the container configuration Map<String, String> containerLabels = containerConfig.getLabels(); // Also, get labels from the image itself final ImageInfo imageInfo = dockerConnector.inspectImage(InspectImageParams.create(image)); Map<String, String> imageLabels = imageInfo.getConfig().getLabels(); // Now merge all labels final Map<String, String> allLabels = new HashMap<>(containerLabels); allLabels.putAll(imageLabels); // Get all ports exposed by the container and by the image // it is under the form "22/tcp" final Set<String> allExposedPorts = ImmutableSet.<String>builder().addAll(containerConfig.getExposedPorts().keySet()) .addAll(imageInfo.getConfig().getExposedPorts().keySet()) .build(); final String[] allEnv = Stream.concat(Arrays.stream(containerConfig.getEnv()), Arrays.stream(imageInfo.getConfig().getEnv())) .toArray(String[]::new); CustomServerEvaluationStrategy.RenderingEvaluation renderingEvaluation = customServerEvaluationStrategy.getOfflineRenderingEvaluation(allLabels, allExposedPorts, allEnv); // portValue is under format <port-number>/<tcp> allExposedPorts.forEach((portValue) -> { final String serviceName = renderingEvaluation.render("service-<serverName>", portValue); final String port = portValue.split("/")[0]; String hostnameAndPort = renderingEvaluation.render(this.template, portValue); // extract only host from host:port String[] elements = hostnameAndPort.split(":"); String hostName = elements[0]; final String host = format("Host:%s", hostName); containerLabels.put(format("traefik.%s.port", serviceName), port); containerLabels.put(format("traefik.%s.frontend.entryPoints", serviceName), "http"); containerLabels.put(format("traefik.%s.frontend.rule", serviceName), host); containerLabels.put("traefik.frontend.rule", createContainerParams.getContainerName()); }); return methodInvocation.proceed(); } /** * Sets the server evaluation provider * * @param serverEvaluationStrategyProvider */ @Inject protected void setServerEvaluationStrategyProvider(ServerEvaluationStrategyProvider serverEvaluationStrategyProvider) { this.serverEvaluationStrategyProvider = serverEvaluationStrategyProvider; } /** * Sets the template of server evaluation strategy * * @param cheDockerCustomExternalTemplate */ @Inject protected void setTemplate( @Nullable @Named("che.docker.server_evaluation_strategy.custom.template") String cheDockerCustomExternalTemplate) { this.template = cheDockerCustomExternalTemplate; } }