Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add container-name configuration for kubernetes extension #24708

Merged
merged 2 commits into from
Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.quarkus.kubernetes.deployment;

import io.dekorate.kubernetes.decorator.AddEnvVarDecorator;
import io.dekorate.kubernetes.decorator.AddInitContainerDecorator;
import io.dekorate.kubernetes.decorator.AddLivenessProbeDecorator;
import io.dekorate.kubernetes.decorator.AddMountDecorator;
import io.dekorate.kubernetes.decorator.AddPortDecorator;
import io.dekorate.kubernetes.decorator.AddReadinessProbeDecorator;
import io.dekorate.kubernetes.decorator.AddSidecarDecorator;
import io.dekorate.kubernetes.decorator.ApplicationContainerDecorator;
import io.dekorate.kubernetes.decorator.ApplyArgsDecorator;
import io.dekorate.kubernetes.decorator.ApplyCommandDecorator;
import io.dekorate.kubernetes.decorator.ApplyImageDecorator;
import io.dekorate.kubernetes.decorator.ApplyImagePullPolicyDecorator;
import io.dekorate.kubernetes.decorator.ApplyLimitsCpuDecorator;
import io.dekorate.kubernetes.decorator.ApplyLimitsMemoryDecorator;
import io.dekorate.kubernetes.decorator.ApplyPortNameDecorator;
import io.dekorate.kubernetes.decorator.ApplyRequestsCpuDecorator;
import io.dekorate.kubernetes.decorator.ApplyRequestsMemoryDecorator;
import io.dekorate.kubernetes.decorator.ApplyWorkingDirDecorator;
import io.dekorate.kubernetes.decorator.Decorator;
import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator;
import io.fabric8.kubernetes.api.model.ContainerFluent;

public class ChangeContainerNameDecorator extends ApplicationContainerDecorator<ContainerFluent<?>> {

private final String name;

public ChangeContainerNameDecorator(String name) {
this.name = name;
}

@Override
public void andThenVisit(ContainerFluent<?> containerFluent) {
containerFluent.withName(name);
}

@Override
public Class<? extends Decorator>[] after() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to find a better way to deal with it, but there are not much we can do atm.

return new Class[] {
ApplyRequestsMemoryDecorator.class, AddEnvVarDecorator.class, AddMountDecorator.class,
AddPortDecorator.class, ApplyArgsDecorator.class, ApplyCommandDecorator.class,
ApplyImagePullPolicyDecorator.class,
ApplyLimitsCpuDecorator.class, ApplyLimitsMemoryDecorator.class, ApplyPortNameDecorator.class,
ApplyRequestsCpuDecorator.class,
ApplyWorkingDirDecorator.class, ResourceProvidingDecorator.class, AddSidecarDecorator.class,
AddInitContainerDecorator.class,
AddLivenessProbeDecorator.class, AddReadinessProbeDecorator.class, ApplyImageDecorator.class
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.quarkus.kubernetes.deployment;

import io.dekorate.kubernetes.decorator.*;
import io.dekorate.kubernetes.decorator.AddSidecarDecorator;
import io.dekorate.openshift.decorator.ApplyDeploymentTriggerDecorator;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.openshift.api.model.DeploymentConfigSpecFluent;

public class ChangeContainerNameInDeploymentTriggerDecorator extends NamedResourceDecorator<DeploymentConfigSpecFluent<?>> {

private final String containerName;

public ChangeContainerNameInDeploymentTriggerDecorator(String containerName) {
this.containerName = containerName;
}

@Override
public void andThenVisit(DeploymentConfigSpecFluent<?> deploymentConfigSpecFluent, ObjectMeta objectMeta) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Respect!

if (deploymentConfigSpecFluent.hasTriggers()) {
deploymentConfigSpecFluent
.editFirstTrigger()
.editImageChangeParams()
.withContainerNames(containerName)
.endImageChangeParams()
.endTrigger()
.buildTriggers();
}
}

@Override
public Class<? extends Decorator>[] after() {
return new Class[] { ApplyDeploymentTriggerDecorator.class, AddEnvVarDecorator.class, AddPortDecorator.class,
AddMountDecorator.class, AddPvcVolumeDecorator.class, AddAwsElasticBlockStoreVolumeDecorator.class,
AddAzureDiskVolumeDecorator.class, AddAwsElasticBlockStoreVolumeDecorator.class, ApplyImageDecorator.class,
ApplyImagePullPolicyDecorator.class, ApplyWorkingDirDecorator.class, ApplyCommandDecorator.class,
ApplyArgsDecorator.class, ApplyServiceAccountNamedDecorator.class, AddReadinessProbeDecorator.class,
AddLivenessProbeDecorator.class, ApplyApplicationContainerDecorator.class, AddSidecarDecorator.class,
AddInitContainerDecorator.class };
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ public class KnativeConfig implements PlatformConfiguration {
@ConfigItem
Map<String, AzureDiskVolumeConfig> azureDiskVolumes;

/**
* If set, it will change the name of the container according to the configuration
*/
@ConfigItem
Optional<String> containerName;

/**
* Init containers
*/
Expand Down Expand Up @@ -264,6 +270,11 @@ public Optional<String> getHost() {
return host;
}

@Override
public Optional<String> getContainerName() {
return containerName;
}

public Map<String, PortConfig> getPorts() {
return ports;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
result.add(new DecoratorBuildItem(KNATIVE, new ApplyContainerImageDecorator(name, i.getImage())));
});

config.getContainerName().ifPresent(containerName -> result
.add(new DecoratorBuildItem(KNATIVE, new ChangeContainerNameDecorator(containerName))));

Stream.concat(config.convertToBuildItems().stream(),
envs.stream().filter(e -> e.getTarget() == null || KNATIVE.equals(e.getTarget()))).forEach(e -> {
result.add(new DecoratorBuildItem(KNATIVE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ public enum DeploymentResourceKind {
@ConfigItem
SecurityContextConfig securityContext;

/**
* If set, it will change the name of the container according to the configuration
*/
@ConfigItem
Optional<String> containerName;

/**
* Debug configuration to be set in pods.
*/
Expand Down Expand Up @@ -395,6 +401,11 @@ public Optional<String> getHost() {
return host;
}

@Override
public Optional<String> getContainerName() {
return containerName;
}

public Map<String, PortConfig> getPorts() {
return ports;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@ public static enum DeploymentResourceKind {
@ConfigItem
ResourcesConfig resources;

/**
* If set, it will change the name of the container according to the configuration
*/
@ConfigItem
Optional<String> containerName;

/**
* If true, an Openshift Route will be created
*
Expand Down Expand Up @@ -351,6 +357,11 @@ public Optional<String> getHost() {
return host;
}

@Override
public Optional<String> getContainerName() {
return containerName;
}

public Integer getReplicas() {
return replicas;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public List<ConfiguratorBuildItem> createConfigurators(ApplicationInfoBuildItem
image.map(ContainerImageInfoBuildItem::getGroup).ifPresent(g -> {
result.add(new ConfiguratorBuildItem(new ApplyImageGroupConfigurator(g)));
});

}
return result;
}
Expand Down Expand Up @@ -226,6 +227,17 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
image.ifPresent(i -> {
result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyContainerImageDecorator(name, i.getImage())));
});
if ((!capabilities.isPresent(Capability.CONTAINER_IMAGE_S2I) && !capabilities.isPresent(Capability.OPENSHIFT))
|| (capabilities.isPresent(Capability.OPENSHIFT) && !(capabilities.isPresent(Capability.CONTAINER_IMAGE_S2I)
|| capabilities.isPresent(Capability.CONTAINER_IMAGE_OPENSHIFT)))) {
result.add(new DecoratorBuildItem(OPENSHIFT, new RemoveDeploymentTriggerDecorator()));
}

config.getContainerName().ifPresent(containerName -> {
result.add(new DecoratorBuildItem(OPENSHIFT, new ChangeContainerNameDecorator(containerName)));
result.add(new DecoratorBuildItem(OPENSHIFT, new ChangeContainerNameInDeploymentTriggerDecorator(containerName)));
});

result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyImagePullPolicyDecorator(name, config.getImagePullPolicy())));
result.add(new DecoratorBuildItem(OPENSHIFT, new AddLabelDecorator(name, OPENSHIFT_APP_RUNTIME, QUARKUS)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public interface PlatformConfiguration extends EnvVarHolder {

Optional<String> getHost();

Optional<String> getContainerName();

Map<String, PortConfig> getPorts();

ServiceType getServiceType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
result.add(new DecoratorBuildItem(KUBERNETES, new RemoveFromMatchingLabelsDecorator(name, Labels.VERSION)));
}

config.getContainerName().ifPresent(containerName -> result
.add(new DecoratorBuildItem(KUBERNETES, new ChangeContainerNameDecorator(containerName))));

// Service handling
result.add(new DecoratorBuildItem(KUBERNETES, new ApplyServiceTypeDecorator(name, config.getServiceType().name())));
if ((config.getServiceType() == ServiceType.NodePort)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.quarkus.it.kubernetes;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.knative.serving.v1.Service;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;

public class KnativeWithSpecifiedContainerNameTest {

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class))
.setApplicationName("knative-with-specified-container-name")
.setApplicationVersion("0.1-SNAPSHOT")
.withConfigurationResource("knative-with-specified-container-name.properties");

@ProdBuildResults
private ProdModeTestResults prodModeTestResults;

@Test
public void assertGeneratedResources() throws IOException {
Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
assertThat(kubernetesDir)
.isDirectoryContaining(p -> p.getFileName().endsWith("knative.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("knative.yml"))
.satisfies(p -> assertThat(p.toFile().listFiles()).hasSize(2));

List<HasMetadata> kubernetesList = DeserializationUtil
.deserializeAsList(kubernetesDir.resolve("knative.yml"));

assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).singleElement().satisfies(i -> {
assertThat(i).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getSpec()).satisfies(spec -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("kfoo");
});

assertThat(spec.getTemplate().getSpec().getContainers()).singleElement().satisfies(container -> {
assertThat(container.getCommand()).contains("my-command");
assertThat(container.getName()).isEqualTo("kbar");
assertThat(container.getArgs()).containsExactly("A", "B");
});
});
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.quarkus.it.kubernetes;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.assertj.core.api.AbstractObjectAssert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.openshift.api.model.DeploymentTriggerPolicy;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.builder.Version;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;

public class KubernetesWithSpecifiedContainerNameTest {

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class))
.setApplicationName("kubernetes-with-specified-container-name")
.setApplicationVersion("0.1-SNAPSHOT")
.withConfigurationResource("kubernetes-with-specified-container-name.properties")
.setForcedDependencies(
Arrays.asList(
new AppArtifact("io.quarkus", "quarkus-kubernetes", Version.getVersion()),
new AppArtifact("io.quarkus", "quarkus-container-image-s2i", Version.getVersion())));

@ProdBuildResults
private ProdModeTestResults prodModeTestResults;

@Test
public void assertGeneratedResources() throws IOException {
Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
assertThat(kubernetesDir)
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml"));

List<HasMetadata> kubernetesList = DeserializationUtil
.deserializeAsList(kubernetesDir.resolve("kubernetes.yml"));
assertThat(kubernetesList.get(0)).isInstanceOfSatisfying(Deployment.class, d -> {
assertThat(d.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("foo");
assertThat(m.getLabels()).contains(entry("app.kubernetes.io/name", "foo"));
});

assertThat(d.getSpec().getTemplate().getSpec().getContainers().get(0))
.satisfies(c -> assertThat(c.getName()).isEqualTo("bar"));
assertThat(d.getSpec().getTemplate().getSpec().getContainers().get(0))
.satisfies(c -> assertThat(c.getCommand()).contains("my-command"));
assertThat(d.getSpec().getTemplate().getSpec().getContainers().get(0))
.satisfies(c -> assertThat(c.getArgs()).containsExactly("A", "B"));
});

List<HasMetadata> openshiftList = DeserializationUtil
.deserializeAsList(kubernetesDir.resolve("openshift.yml"));
assertThat(openshiftList).filteredOn(h -> "DeploymentConfig".equals(h.getKind())).singleElement().satisfies(h -> {
AbstractObjectAssert<?, ?> specAssert = assertThat(h).extracting("spec");
specAssert.extracting("template").extracting("spec").isInstanceOfSatisfying(PodSpec.class,
podSpec -> {
assertThat(podSpec.getContainers()).singleElement().satisfies(container -> {
assertThat(container.getCommand())
.contains("my-openshift-command");
assertThat(container.getArgs()).containsExactly("C", "D");
assertThat(container)
.satisfies(c -> assertThat(c.getName()).isEqualTo("obar"));
});
});

specAssert.extracting("triggers").isInstanceOfSatisfying(Collection.class, c -> {
assertThat(c).singleElement().satisfies(trigger -> {
assertThat(((DeploymentTriggerPolicy) trigger).getImageChangeParams().getContainerNames())
.contains("obar");
});
});

assertThat(h.getMetadata().getName()).isIn("ofoo", "foo", "openjdk-11");
assertThat(h.getMetadata().getLabels()).contains(entry("app.kubernetes.io/name", "ofoo"));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Configuration file
quarkus.kubernetes.deployment-target=knative
quarkus.knative.name=kfoo
quarkus.knative.container-name=kbar
quarkus.knative.command=my-command
quarkus.knative.arguments=A,B
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
quarkus.kubernetes.deployment-target=kubernetes,openshift

quarkus.application.name=test
quarkus.kubernetes.name=foo
quarkus.kubernetes.container-name=bar
quarkus.kubernetes.command=my-command
quarkus.kubernetes.arguments=A,B

quarkus.openshift.name=ofoo
quarkus.openshift.container-name=obar
quarkus.openshift.command=my-openshift-command
quarkus.openshift.arguments=C,D

quarkus.s2i.name=s2ifoo