refactor: Move KubernetesClientBuildConfig to an spi module so that it

can be used by other extensions.
This commit is contained in:
Ioannis Canellos
2020-02-05 13:45:08 +02:00
parent 6397660875
commit 5083cf2f75
25 changed files with 468 additions and 111 deletions

View File

@@ -478,7 +478,7 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-docker-spi</artifactId>
<artifactId>quarkus-container-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@@ -491,6 +491,11 @@
<artifactId>quarkus-kubernetes-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client-deployment</artifactId>

View File

@@ -33,7 +33,6 @@ import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem;
import io.quarkus.deployment.pkg.builditem.ContainerImageResultBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.pkg.builditem.DeploymentResultBuildItem;
import io.quarkus.runtime.LaunchMode;
@@ -110,7 +109,6 @@ public class QuarkusAugmentor {
}
chainBuilder.addFinal(GeneratedClassBuildItem.class)
.addFinal(GeneratedResourceBuildItem.class)
.addFinal(ContainerImageResultBuildItem.class)
.addFinal(DeploymentResultBuildItem.class);
for (Consumer<BuildChainBuilder> i : buildChainCustomizers) {

View File

@@ -1,21 +0,0 @@
package io.quarkus.deployment.pkg;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
@ConfigRoot
public class ContainerConfig {
/**
* Flag that specifies if container build is enabled
*/
@ConfigItem
public boolean build;
/**
* Flag that specifies if container deploy is enabled
*/
@ConfigItem
public boolean deploy;
}

View File

@@ -17,6 +17,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-spi</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,45 @@
package io.quarkus.container.deployment;
import java.util.Optional;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
@ConfigRoot
public class ContainerConfig {
/**
* The container registry to use
*/
@ConfigItem
public Optional<String> registry;
/**
* The group the container image will be part of
*/
@ConfigItem(defaultValue = "${user.name}")
public String group;
/**
* The name of the container image. If not set defaults to the application name
*/
@ConfigItem
public Optional<String> name;
/**
* The tag of the container image. If not set defaults to the application version
*/
@ConfigItem
public Optional<String> tag;
/**
* Flag that specifies if container build is enabled
*/
@ConfigItem
public boolean build;
/**
* Flag that specifies if container deploy is enabled
*/
@ConfigItem
public boolean deploy;
}

View File

@@ -0,0 +1,35 @@
package io.quarkus.container.deployment;
import io.quarkus.container.deployment.util.ImageUtil;
import io.quarkus.container.spi.ContainerImageBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;
import io.quarkus.deployment.pkg.steps.NativeBuild;
public class ContainerProcessor {
private ContainerConfig containerConfig;
@BuildStep(onlyIfNot = NativeBuild.class)
public ContainerImageBuildItem publishImageInfo(ApplicationInfoBuildItem app) {
String image = ImageUtil.getImage(containerConfig.registry, containerConfig.group,
containerConfig.name.orElse(app.getName()), containerConfig.tag.orElse(app.getVersion()));
return new ContainerImageBuildItem(image);
}
@BuildStep(onlyIf = NativeBuild.class)
public ContainerImageBuildItem publishNativeImageInfo(ApplicationInfoBuildItem app) {
String image = ImageUtil.getImage(containerConfig.registry, containerConfig.group,
containerConfig.name.orElse(app.getName()), containerConfig.tag.orElse(app.getVersion() + "-native"));
return new ContainerImageBuildItem(image);
}
public ContainerConfig getContainerConfig() {
return this.containerConfig;
}
public void setContainerConfig(ContainerConfig containerConfig) {
this.containerConfig = containerConfig;
}
}

View File

@@ -1,13 +1,23 @@
package io.quarkus.container.deployment;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import org.jboss.logging.Logger;
import io.quarkus.deployment.pkg.ContainerConfig;
import io.quarkus.deployment.util.ExecUtil;
public class DockerBuild implements BooleanSupplier {
private static final Logger LOGGER = Logger.getLogger(DockerBuild.class.getName());
private static boolean daemonFound = false;
private final ContainerConfig containerConfig;
DockerBuild(ContainerConfig containerConfig) {
@@ -17,12 +27,48 @@ public class DockerBuild implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
if (containerConfig.build) {
//No need to perform the check multiple times.
if (daemonFound) {
return true;
}
try {
return ExecUtil.exec("docker", "version");
OutputFilter filter = new OutputFilter();
if (ExecUtil.exec(new File("."), filter, "docker", "version", "--format", "'{{.Server.Version}}'")) {
LOGGER.info("Docker daemon found! Version:" + filter.getOutput());
daemonFound = true;
return true;
} else {
LOGGER.warn("Could not connect to docker daemon!");
return false;
}
} catch (Exception e) {
LOGGER.warn("Could not connect to docker daemon!");
return false;
}
}
return false;
}
public static class OutputFilter implements Function<InputStream, Runnable> {
private final StringBuilder builder = new StringBuilder();
@Override
public Runnable apply(InputStream is) {
return () -> {
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
builder.append(line);
}
} catch (IOException e) {
throw new RuntimeException("Error reading stream.", e);
}
};
}
public String getOutput() {
return builder.toString();
}
}
}

View File

@@ -8,22 +8,27 @@ import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import javax.inject.Inject;
import org.jboss.logging.Logger;
import io.quarkus.container.deployment.util.ImageUtil;
import io.quarkus.container.spi.ContainerImageBuildItem;
import io.quarkus.container.spi.ContainerImageResultBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;
import io.quarkus.deployment.pkg.builditem.ContainerImageBuildItem;
import io.quarkus.deployment.pkg.builditem.ContainerImageResultBuildItem;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.JarBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.deployment.pkg.steps.NativeBuild;
import io.quarkus.deployment.util.ExecUtil;
import io.quarkus.deployment.util.ImageUtil;
public class DockerBuildStep {
@@ -31,13 +36,14 @@ public class DockerBuildStep {
private static final String DOCKERFILE_JVM = "Dockerfile.jvm";
private static final String DOCKERFILE_NATIVE = "Dockerfile.native";
@Inject
BuildProducer<ArtifactResultBuildItem> artifact;
@BuildStep(onlyIf = DockerBuild.class, onlyIfNot = NativeBuild.class)
public ContainerImageResultBuildItem dockerBuildFromJar(ApplicationInfoBuildItem app,
OutputTargetBuildItem out,
Optional<ContainerImageBuildItem> dockerImage,
JarBuildItem artifact) {
ContainerImageBuildItem containerImage, JarBuildItem jar) {
log.info("Building docker image for jar.");
String image = dockerImage.map(d -> d.getImage()).orElse(app.getName() + ":" + app.getVersion());
ImageIdReader reader = new ImageIdReader();
Path dockerFile = extractDockerfile(DOCKERFILE_JVM);
ExecUtil.exec(out.getOutputDirectory().toFile(),
@@ -45,19 +51,21 @@ public class DockerBuildStep {
"docker", "build",
"-f",
dockerFile.resolve(DOCKERFILE_JVM).toAbsolutePath().toString(),
"-t", image,
"-t", containerImage.getImage(),
out.getOutputDirectory().toAbsolutePath().toString());
return new ContainerImageResultBuildItem(reader.getImageId(), ImageUtil.getRepository(image), ImageUtil.getTag(image));
artifact.produce(new ArtifactResultBuildItem(null, "jar-container", new HashMap<String, Path>()));
return new ContainerImageResultBuildItem(reader.getImageId(), ImageUtil.getRepository(containerImage.getImage()),
ImageUtil.getTag(containerImage.getImage()));
}
@BuildStep(onlyIf = { DockerBuild.class, NativeBuild.class })
public ContainerImageResultBuildItem dockerBuildFromNativeImage(ApplicationInfoBuildItem app,
ContainerImageBuildItem containerImage,
OutputTargetBuildItem out,
Optional<ContainerImageBuildItem> dockerImage,
NativeImageBuildItem nativeImage) {
log.info("Building docker image for native image.");
String image = dockerImage.map(d -> d.getImage()).orElse(app.getName() + ":" + app.getVersion() + "-native");
Path dockerFile = extractDockerfile(DOCKERFILE_NATIVE);
ImageIdReader reader = new ImageIdReader();
ExecUtil.exec(out.getOutputDirectory().toFile(),
@@ -65,9 +73,11 @@ public class DockerBuildStep {
"docker", "build",
"-f",
dockerFile.resolve(DOCKERFILE_NATIVE).toAbsolutePath().toString(),
"-t", image,
"-t", containerImage.getImage(),
out.getOutputDirectory().toAbsolutePath().toString());
return new ContainerImageResultBuildItem(reader.getImageId(), ImageUtil.getRepository(image), ImageUtil.getTag(image));
artifact.produce(new ArtifactResultBuildItem(null, "native-container", new HashMap<String, Path>()));
return new ContainerImageResultBuildItem(reader.getImageId(), ImageUtil.getRepository(containerImage.getImage()),
ImageUtil.getTag(containerImage.getImage()));
}
private Path extractDockerfile(String resource) {
@@ -120,4 +130,5 @@ public class DockerBuildStep {
};
}
}
}

View File

@@ -1,11 +1,37 @@
package io.quarkus.deployment.util;
package io.quarkus.container.deployment.util;
import java.util.Optional;
public class ImageUtil {
private static final String SLASH = "/";
private static final String COLN = ":";
/**
* Create an image from the individual parts.
*
* @param registry The registry.
* @param repository The repository.
* @param name The name.
* @param tag The tag.
* @return The image.
*/
public static String getImage(Optional<String> registry, String repository, String name, String tag) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Docker image name cannot be null!");
}
if (tag == null || tag.isEmpty()) {
throw new IllegalArgumentException("Docker image tag cannot be null!");
}
StringBuilder sb = new StringBuilder();
registry.ifPresent(r -> sb.append(r).append(SLASH));
sb.append(repository).append(SLASH);
sb.append(name).append(COLN).append(tag);
return sb.toString();
}
/**
* Return the registry part of the docker image.
*

View File

@@ -3,11 +3,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-build-parent</artifactId>
<artifactId>quarkus-container-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../../build-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>quarkus-container-parent</artifactId>

View File

@@ -1,5 +1,5 @@
package io.quarkus.deployment.pkg.builditem;
package io.quarkus.container.spi;
import io.quarkus.builder.item.SimpleBuildItem;

View File

@@ -1,5 +1,4 @@
package io.quarkus.deployment.pkg.builditem;
package io.quarkus.container.spi;
import io.quarkus.builder.item.SimpleBuildItem;
@@ -26,5 +25,4 @@ public final class ContainerImageResultBuildItem extends SimpleBuildItem {
public String getTag() {
return this.tag;
}
}

View File

@@ -24,6 +24,10 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>

View File

@@ -0,0 +1,17 @@
package io.quarkus.kubernetes.client.deployment;
import static io.quarkus.kubernetes.client.runtime.KubernetesClientUtils.*;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.kubernetes.client.runtime.KubernetesClientBuildConfig;
import io.quarkus.kubernetes.client.spi.KubernetesClientBuildItem;
public class KubernetesClientBuildStep {
private KubernetesClientBuildConfig buildConfig;
@BuildStep
public KubernetesClientBuildItem process() {
return new KubernetesClientBuildItem(createClient(buildConfig));
}
}

View File

@@ -16,5 +16,6 @@
<modules>
<module>deployment</module>
<module>runtime</module>
<module>spi</module>
</modules>
</project>

View File

@@ -14,138 +14,139 @@ public class KubernetesClientBuildConfig {
* Whether or not the client should trust a self signed certificate if so presented by the API server
*/
@ConfigItem(defaultValue = "false")
boolean trustCerts;
public boolean trustCerts;
/**
* URL of the Kubernetes API server
*/
@ConfigItem
Optional<String> masterUrl;
public Optional<String> masterUrl;
/**
* Default namespace to use
*/
@ConfigItem
Optional<String> namespace;
public Optional<String> namespace;
/**
* CA certificate file
*/
@ConfigItem
Optional<String> caCertFile;
public Optional<String> caCertFile;
/**
* CA certificate data
*/
@ConfigItem
Optional<String> caCertData;
public Optional<String> caCertData;
/**
* Client certificate file
*/
@ConfigItem
Optional<String> clientCertFile;
public Optional<String> clientCertFile;
/**
* Client certificate data
*/
@ConfigItem
Optional<String> clientCertData;
public Optional<String> clientCertData;
/**
* Client key file
*/
@ConfigItem
Optional<String> clientKeyFile;
public Optional<String> clientKeyFile;
/**
* Client key data
*/
@ConfigItem
Optional<String> clientKeyData;
public Optional<String> clientKeyData;
/**
* Client key algorithm
*/
@ConfigItem
Optional<String> clientKeyAlgo;
public Optional<String> clientKeyAlgo;
/**
* Client key passphrase
*/
@ConfigItem
Optional<String> clientKeyPassphrase;
public Optional<String> clientKeyPassphrase;
/**
* Kubernetes auth username
*/
@ConfigItem
Optional<String> username;
public Optional<String> username;
/**
* Kubernetes auth password
*/
@ConfigItem
Optional<String> password;
public Optional<String> password;
/**
* Watch reconnect interval
*/
@ConfigItem(defaultValue = "PT1S") // default lifted from Kubernetes Client
Duration watchReconnectInterval;
public Duration watchReconnectInterval;
/**
* Maximum reconnect attempts in case of watch failure
* By default there is no limit to the number of reconnect attempts
*/
@ConfigItem(defaultValue = "-1") // default lifted from Kubernetes Client
int watchReconnectLimit;
public int watchReconnectLimit;
/**
* Maximum amount of time to wait for a connection with the API server to be established
*/
@ConfigItem(defaultValue = "PT10S") // default lifted from Kubernetes Client
Duration connectionTimeout;
public Duration connectionTimeout;
/**
* Maximum amount of time to wait for a request to the API server to be completed
*/
@ConfigItem(defaultValue = "PT10S") // default lifted from Kubernetes Client
Duration requestTimeout;
public Duration requestTimeout;
/**
* Maximum amount of time in milliseconds to wait for a rollout to be completed
*/
@ConfigItem(defaultValue = "PT15M") // default lifted from Kubernetes Client
Duration rollingTimeout;
public Duration rollingTimeout;
/**
* HTTP proxy used to access the Kubernetes API server
*/
@ConfigItem
Optional<String> httpProxy;
public Optional<String> httpProxy;
/**
* HTTPS proxy used to access the Kubernetes API server
*/
@ConfigItem
Optional<String> httpsProxy;
public Optional<String> httpsProxy;
/**
* Proxy username
*/
@ConfigItem
Optional<String> proxyUsername;
public Optional<String> proxyUsername;
/**
* Proxy password
*/
@ConfigItem
Optional<String> proxyPassword;
public Optional<String> proxyPassword;
/**
* IP addresses or hosts to exclude from proxying
*/
@ConfigItem
Optional<String[]> noProxy;
public Optional<String[]> noProxy;
}

View File

@@ -5,7 +5,6 @@ import javax.enterprise.inject.Produces;
import javax.inject.Singleton;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.quarkus.arc.DefaultBean;
@@ -19,33 +18,7 @@ public class KubernetesClientProducer {
@Singleton
@Produces
public Config config() {
Config base = Config.autoConfigure(null);
return new ConfigBuilder(base)
.withTrustCerts(buildConfig.trustCerts)
.withWatchReconnectInterval((int) buildConfig.watchReconnectInterval.toMillis())
.withWatchReconnectLimit(buildConfig.watchReconnectLimit)
.withConnectionTimeout((int) buildConfig.connectionTimeout.toMillis())
.withRequestTimeout((int) buildConfig.requestTimeout.toMillis())
.withRollingTimeout(buildConfig.rollingTimeout.toMillis())
.withMasterUrl(buildConfig.masterUrl.orElse(base.getMasterUrl()))
.withNamespace(buildConfig.namespace.orElse(base.getNamespace()))
.withUsername(buildConfig.username.orElse(base.getUsername()))
.withPassword(buildConfig.password.orElse(base.getPassword()))
.withCaCertFile(buildConfig.caCertFile.orElse(base.getCaCertFile()))
.withCaCertData(buildConfig.caCertData.orElse(base.getCaCertData()))
.withClientCertFile(buildConfig.clientCertFile.orElse(base.getClientCertFile()))
.withClientCertData(buildConfig.clientCertData.orElse(base.getClientCertData()))
.withClientKeyFile(buildConfig.clientKeyFile.orElse(base.getClientKeyFile()))
.withClientKeyData(buildConfig.clientKeyData.orElse(base.getClientKeyData()))
.withClientKeyPassphrase(buildConfig.clientKeyPassphrase.orElse(base.getClientKeyPassphrase()))
.withClientKeyAlgo(buildConfig.clientKeyAlgo.orElse(base.getClientKeyAlgo()))
.withHttpProxy(buildConfig.httpProxy.orElse(base.getHttpProxy()))
.withHttpsProxy(buildConfig.httpsProxy.orElse(base.getHttpsProxy()))
.withProxyUsername(buildConfig.proxyUsername.orElse(base.getProxyUsername()))
.withProxyPassword(buildConfig.proxyPassword.orElse(base.getProxyPassword()))
.withNoProxy(buildConfig.noProxy.isPresent() ? buildConfig.noProxy.get() : base.getNoProxy())
.build();
return KubernetesClientUtils.createConfig(buildConfig);
}
@DefaultBean

View File

@@ -0,0 +1,42 @@
package io.quarkus.kubernetes.client.runtime;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
public class KubernetesClientUtils {
public static Config createConfig(KubernetesClientBuildConfig buildConfig) {
Config base = new Config();
return new ConfigBuilder()
.withTrustCerts(buildConfig.trustCerts)
.withWatchReconnectInterval((int) buildConfig.watchReconnectInterval.toMillis())
.withWatchReconnectLimit(buildConfig.watchReconnectLimit)
.withConnectionTimeout((int) buildConfig.connectionTimeout.toMillis())
.withRequestTimeout((int) buildConfig.requestTimeout.toMillis())
.withRollingTimeout(buildConfig.rollingTimeout.toMillis())
.withMasterUrl(buildConfig.masterUrl.orElse(base.getMasterUrl()))
.withNamespace(buildConfig.namespace.orElse(base.getNamespace()))
.withUsername(buildConfig.username.orElse(base.getUsername()))
.withPassword(buildConfig.password.orElse(base.getPassword()))
.withCaCertFile(buildConfig.caCertFile.orElse(base.getCaCertFile()))
.withCaCertData(buildConfig.caCertData.orElse(base.getCaCertData()))
.withClientCertFile(buildConfig.clientCertFile.orElse(base.getClientCertFile()))
.withClientCertData(buildConfig.clientCertData.orElse(base.getClientCertData()))
.withClientKeyFile(buildConfig.clientKeyFile.orElse(base.getClientKeyFile()))
.withClientKeyData(buildConfig.clientKeyData.orElse(base.getClientKeyData()))
.withClientKeyPassphrase(buildConfig.clientKeyPassphrase.orElse(base.getClientKeyPassphrase()))
.withClientKeyAlgo(buildConfig.clientKeyAlgo.orElse(base.getClientKeyAlgo()))
.withHttpProxy(buildConfig.httpProxy.orElse(base.getHttpProxy()))
.withHttpsProxy(buildConfig.httpsProxy.orElse(base.getHttpsProxy()))
.withProxyUsername(buildConfig.proxyUsername.orElse(base.getProxyUsername()))
.withProxyPassword(buildConfig.proxyPassword.orElse(base.getProxyPassword()))
.withNoProxy(buildConfig.noProxy.isPresent() ? buildConfig.noProxy.get() : base.getNoProxy())
.build();
}
public static KubernetesClient createClient(KubernetesClientBuildConfig buildConfig) {
return new DefaultKubernetesClient(createConfig(buildConfig));
}
}

View File

@@ -0,0 +1,57 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-kubernetes-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>quarkus-kubernetes-client-spi</artifactId>
<name>Quarkus - Kubernetes Client - SPI</name>
<description>Extensions that use the Kubernetes client, use this module to
configure the client instance</description>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<exclusions>
<exclusion>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</exclusion>
<exclusion>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,18 @@
package io.quarkus.kubernetes.client.spi;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.quarkus.builder.item.SimpleBuildItem;
public final class KubernetesClientBuildItem extends SimpleBuildItem {
private final KubernetesClient client;
public KubernetesClientBuildItem(KubernetesClient client) {
this.client = client;
}
public KubernetesClient getClient() {
return this.client;
}
}

View File

@@ -17,10 +17,18 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.dekorate</groupId>
<artifactId>kubernetes-annotations</artifactId>

View File

@@ -3,8 +3,17 @@ package io.quarkus.kubernetes.deployment;
public enum DeploymentTarget {
KUBERNETES,
OPENSHIFT,
KNATIVE
KUBERNETES("Deployment"),
OPENSHIFT("DeploymentConfig"),
KNATIVE("Service");
private String kind;
DeploymentTarget(String kind) {
this.kind = kind;
}
public String getKind() {
return this.kind;
}
}

View File

@@ -1,7 +1,18 @@
package io.quarkus.kubernetes.deployment;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import org.jboss.logging.Logger;
import io.quarkus.container.deployment.ContainerConfig;
import io.quarkus.container.deployment.DockerBuild.OutputFilter;
@@ -9,6 +20,8 @@ import io.quarkus.deployment.util.ExecUtil;
public class KubernetesDeploy implements BooleanSupplier {
private final Logger LOGGER = Logger.getLogger(KubernetesDeploy.class);
private KubernetesConfig kubernetesConfig;
private ContainerConfig containerConfig;
@@ -20,15 +33,65 @@ public class KubernetesDeploy implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
if (containerConfig.deploy) {
OutputFilter filter = new OutputFilter();
try {
if (kubernetesConfig.getDeploymentTarget().contains(DeploymentTarget.OPENSHIFT)) {
return ExecUtil.exec("oc", "version");
if (ExecUtil.exec(new File("."), filter, "oc", "version")) {
Optional<String> version = getServerVersionFromOc(filter.getLines());
version.ifPresent(v -> LOGGER.info("Found Kubernetes version:" + v));
return true;
}
}
if (ExecUtil.exec(new File("."), filter, "kubectl", "version")) {
Optional<String> version = getServerVersionFromKubectl(filter.getLines());
version.ifPresent(v -> LOGGER.info("Found Kubernetes version:" + v));
return true;
}
return ExecUtil.exec("kubectl", "version");
} catch (Exception e) {
return false;
}
}
return false;
}
private static Optional<String> getServerVersionFromOc(List<String> lines) {
return lines.stream()
.filter(l -> l.startsWith("kubernetes"))
.map(l -> l.split(" "))
.filter(a -> a.length > 2)
.map(a -> a[1])
.findFirst();
}
private static Optional<String> getServerVersionFromKubectl(List<String> lines) {
return lines.stream()
.filter(l -> l.startsWith("Server Version"))
.map(l -> l.split("\""))
.filter(a -> a.length > 5)
.map(a -> a[5])
.findFirst();
}
private static class OutputFilter implements Function<InputStream, Runnable> {
private final List<String> list = new ArrayList();
@Override
public Runnable apply(InputStream is) {
return () -> {
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
list.add(line);
}
} catch (IOException e) {
throw new RuntimeException("Error reading stream.", e);
}
};
}
public List<String> getLines() {
return list;
}
}
}

View File

@@ -1,6 +1,13 @@
package io.quarkus.kubernetes.deployment;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import org.jboss.logging.Logger;
import io.dekorate.deps.kubernetes.api.model.HasMetadata;
@@ -13,10 +20,9 @@ import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationInfoBuildItem;
import io.quarkus.deployment.pkg.builditem.ContainerImageResultBuildItem;
import io.quarkus.deployment.pkg.builditem.DeploymentResultBuildItem;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.deployment.util.ExecUtil;
import io.quarkus.kubernetes.client.spi.KubernetesClientBuildItem;
public class KubernetesDeployer {
@@ -29,13 +35,26 @@ public class KubernetesDeployer {
OutputTargetBuildItem outputTarget,
BuildProducer<DeploymentResultBuildItem> deploymentResult) {
kubernetesConfig.getDeploymentTarget().stream().map(Enum::name).map(String::toLowerCase).findFirst().ifPresent(d -> {
LOG.info("Deploying to " + d + ".");
ExecUtil.exec(outputTarget.getOutputDirectory().toFile(), "oc", "apply", "-f",
"kubernetes/" + d + ".yml");
});
return kubernetesConfig.getDeploymentTarget().stream().findFirst().map(d -> {
String namespace = Optional.of(kubernetesClient.getClient().getNamespace()).orElse("default");
return new DeploymentResultBuildItem(null, null);
LOG.info("Deploying to " + d.name().toLowerCase() + "in namespace:" + namespace + ".");
File manifest = outputTarget.getOutputDirectory().resolve("kubernetes").resolve(d.name().toLowerCase() + ".yml")
.toFile();
try (FileInputStream fis = new FileInputStream(manifest)) {
List<HasMetadata> resources = kubernetesClient.getClient().load(fis).inNamespace(namespace).createOrReplace();
HasMetadata m = resources.stream()
.filter(r -> r.getKind().equals(d.getKind()))
.findFirst()
.orElseThrow(() -> new IllegalStateException(
"No " + d.getKind() + " found under: " + manifest.getAbsolutePath()));
return new DeploymentResultBuildItem(m.getMetadata().getName(), m.getMetadata().getLabels());
} catch (FileNotFoundException e) {
throw new IllegalStateException("Can't find generated kubernetes manifest: " + manifest.getAbsolutePath());
} catch (IOException e) {
throw new RuntimeException("Error closing file: " + manifest.getAbsolutePath());
}
}).get();
}
public void setKubernetesconfig(KubernetesConfig kubernetesConfig) {

View File

@@ -22,7 +22,6 @@ import org.eclipse.microprofile.config.ConfigProvider;
import io.dekorate.Session;
import io.dekorate.SessionWriter;
import io.dekorate.kubernetes.config.ImageConfiguration;
import io.dekorate.kubernetes.config.PortBuilder;
import io.dekorate.kubernetes.config.ProbeBuilder;
import io.dekorate.kubernetes.configurator.AddPort;
@@ -36,9 +35,9 @@ import io.dekorate.processor.SimpleFileWriter;
import io.dekorate.project.BuildInfo;
import io.dekorate.project.FileProjectFactory;
import io.dekorate.project.Project;
import io.dekorate.utils.Images;
import io.dekorate.utils.Maps;
import io.dekorate.utils.Strings;
import io.quarkus.container.spi.ContainerImageBuildItem;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
@@ -47,7 +46,6 @@ import io.quarkus.deployment.builditem.ArchiveRootBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.GeneratedFileSystemResourceBuildItem;
import io.quarkus.deployment.pkg.PackageConfig;
import io.quarkus.deployment.pkg.builditem.ContainerImageBuildItem;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.kubernetes.spi.KubernetesHealthLivenessPathBuildItem;
import io.quarkus.kubernetes.spi.KubernetesHealthReadinessPathBuildItem;