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 support for specifying the namespace of the generated k8s resources #10674

Merged
merged 1 commit into from
Jul 13, 2020
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,45 @@
package io.quarkus.kubernetes.deployment;

import java.util.Objects;

import io.dekorate.deps.kubernetes.api.model.ObjectMeta;
import io.dekorate.deps.kubernetes.api.model.ObjectMetaBuilder;
import io.dekorate.kubernetes.decorator.AddSidecarDecorator;
import io.dekorate.kubernetes.decorator.ContainerDecorator;
import io.dekorate.kubernetes.decorator.Decorator;
import io.dekorate.kubernetes.decorator.NamedResourceDecorator;
import io.dekorate.kubernetes.decorator.ResourceProvidingDecorator;

public class AddNamespaceDecorator extends NamedResourceDecorator<ObjectMetaBuilder> {

private final String namespace;

public AddNamespaceDecorator(String namespace) {
this.namespace = Objects.requireNonNull(namespace);
}

@Override
public void andThenVisit(ObjectMetaBuilder builder, ObjectMeta resourceMeta) {
builder.withNamespace(namespace);
}

@Override
public Class<? extends Decorator>[] after() {
return new Class[] { ResourceProvidingDecorator.class, ContainerDecorator.class, AddSidecarDecorator.class };
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
AddNamespaceDecorator that = (AddNamespaceDecorator) o;
return namespace.equals(that.namespace);
}

@Override
public int hashCode() {
return Objects.hash(namespace);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ public class KnativeConfig implements PlatformConfiguration {
@ConfigItem(defaultValue = "${quarkus.container-image.tag}")
Optional<String> version;

/**
* The namespace the generated resources should belong to.
* If not value is set, then the 'namespace' field will not be
* added to the 'metadata' section of the generated manifests.
* This in turn means that when the manifests are applied to a cluster,
* the namespace will be resolved from the current Kubernetes context
* (see https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context
* for more details).
*/
@ConfigItem
Optional<String> namespace;

/**
* Custom labels to add to all resources
*/
Expand Down Expand Up @@ -191,6 +203,10 @@ public Optional<String> getVersion() {
return version;
}

public Optional<String> getNamespace() {
return namespace;
}

public Map<String, String> getLabels() {
return labels;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ public class KubernetesConfig implements PlatformConfiguration {
@ConfigItem(defaultValue = "${quarkus.container-image.tag}")
Optional<String> version;

/**
* The namespace the generated resources should belong to.
* If not value is set, then the 'namespace' field will not be
* added to the 'metadata' section of the generated manifests.
* This in turn means that when the manifests are applied to a cluster,
* the namespace will be resolved from the current Kubernetes context
* (see https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context
* for more details).
*/
@ConfigItem
Optional<String> namespace;

/**
* Custom labels to add to all resources
*/
Expand Down Expand Up @@ -219,6 +231,10 @@ public Optional<String> getVersion() {
return version;
}

public Optional<String> getNamespace() {
return namespace;
}

public Map<String, String> getLabels() {
return labels;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,27 +415,11 @@ private void applyConfig(Session session, Project project, String target, String
session.resources().decorate(OPENSHIFT, new AddLabelDecorator(new Label(OPENSHIFT_APP_RUNTIME, QUARKUS)));
}

ScmInfo scm = project.getScmInfo();
String vcsUrl = scm != null ? scm.getUrl() : null;
String commitId = scm != null ? scm.getCommit() : null;

//Dekorate uses its own annotations. Let's replace them with the quarkus ones.
session.resources().decorate(target, new RemoveAnnotationDecorator(Annotations.VCS_URL));
session.resources().decorate(target, new RemoveAnnotationDecorator(Annotations.COMMIT_ID));
//Add quarkus vcs annotations
if (commitId != null) {
session.resources().decorate(target,
new AddAnnotationDecorator(new Annotation(QUARKUS_ANNOTATIONS_COMMIT_ID, commitId)));
}
if (vcsUrl != null) {
session.resources().decorate(target,
new AddAnnotationDecorator(new Annotation(QUARKUS_ANNOTATIONS_VCS_URL, vcsUrl)));
if (config.getNamespace().isPresent()) {
session.resources().decorate(target, new AddNamespaceDecorator(config.getNamespace().get()));
}

if (config.isAddBuildTimestamp()) {
session.resources().decorate(target, new AddAnnotationDecorator(new Annotation(QUARKUS_ANNOTATIONS_BUILD_TIMESTAMP,
now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd - HH:mm:ss Z")))));
}
applyAnnotations(session, project, target, config, now);

config.getWorkingDir().ifPresent(w -> {
session.resources().decorate(target, new ApplyWorkingDirDecorator(name, w));
Expand Down Expand Up @@ -505,6 +489,31 @@ private void applyConfig(Session session, Project project, String target, String
session.resources().decorate(target, new RemoveOptionalFromConfigMapKeySelectorDecorator());
}

private void applyAnnotations(Session session, Project project, String target, PlatformConfiguration config,
ZonedDateTime now) {
ScmInfo scm = project.getScmInfo();
String vcsUrl = scm != null ? scm.getUrl() : null;
String commitId = scm != null ? scm.getCommit() : null;

//Dekorate uses its own annotations. Let's replace them with the quarkus ones.
session.resources().decorate(target, new RemoveAnnotationDecorator(Annotations.VCS_URL));
session.resources().decorate(target, new RemoveAnnotationDecorator(Annotations.COMMIT_ID));
//Add quarkus vcs annotations
if (commitId != null) {
session.resources().decorate(target,
new AddAnnotationDecorator(new Annotation(QUARKUS_ANNOTATIONS_COMMIT_ID, commitId)));
}
if (vcsUrl != null) {
session.resources().decorate(target,
new AddAnnotationDecorator(new Annotation(QUARKUS_ANNOTATIONS_VCS_URL, vcsUrl)));
}

if (config.isAddBuildTimestamp()) {
session.resources().decorate(target, new AddAnnotationDecorator(new Annotation(QUARKUS_ANNOTATIONS_BUILD_TIMESTAMP,
now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd - HH:mm:ss Z")))));
}
}

private void applyKnativeConfig(Session session, Project project, String name, KnativeConfig config) {
if (config.clusterLocal) {
session.resources().decorate(KNATIVE, new AddLabelDecorator(name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ public class OpenshiftConfig implements PlatformConfiguration {
@ConfigItem(defaultValue = "${quarkus.container-image.tag}")
Optional<String> version;

/**
* The namespace the generated resources should belong to.
* If not value is set, then the 'namespace' field will not be
* added to the 'metadata' section of the generated manifests.
* This in turn means that when the manifests are applied to a cluster,
* the namespace will be resolved from the current Kubernetes context
* (see https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context
* for more details).
*/
@ConfigItem
Optional<String> namespace;

/**
* Custom labels to add to all resources
*/
Expand Down Expand Up @@ -211,6 +223,10 @@ public Optional<String> getVersion() {
return version;
}

public Optional<String> getNamespace() {
return namespace;
}

public Map<String, String> getLabels() {
return labels;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface PlatformConfiguration extends EnvVarHolder {

Optional<String> getVersion();

Optional<String> getNamespace();

Map<String, String> getLabels();

Map<String, String> getAnnotations();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public void assertGeneratedResources() throws IOException {
assertThat(kubernetesList.get(0)).isInstanceOfSatisfying(Deployment.class, d -> {
assertThat(d.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("basic");
assertThat(m.getNamespace()).isNull();
});

assertThat(d.getSpec()).satisfies(deploymentSpec -> {
Expand All @@ -82,13 +83,20 @@ public void assertGeneratedResources() throws IOException {
});

assertThat(kubernetesList.get(1)).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getNamespace()).isNull();
});
assertThat(s.getSpec()).satisfies(spec -> {
assertThat(spec.getPorts()).hasSize(1).hasOnlyOneElementSatisfying(p -> {
assertThat(p.getPort()).isEqualTo(8080);
});
});
});

assertThat(kubernetesList.get(2)).isInstanceOf(ServiceAccount.class);
assertThat(kubernetesList.get(2)).isInstanceOfSatisfying(ServiceAccount.class, sa -> {
assertThat(sa.getMetadata()).satisfies(m -> {
assertThat(m.getNamespace()).isNull();
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.Service;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;
Expand Down Expand Up @@ -44,6 +45,7 @@ public void assertGeneratedResources() throws IOException {
assertThat(h.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("basic-openshift");
assertThat(m.getLabels().get("app.openshift.io/runtime")).isEqualTo("quarkus");
assertThat(m.getNamespace()).isNull();
});
assertThat(h).extracting("spec").extracting("replicas").isEqualTo(1);
assertThat(h).extracting("spec").extracting("template").extracting("spec").isInstanceOfSatisfying(PodSpec.class,
Expand All @@ -55,5 +57,13 @@ public void assertGeneratedResources() throws IOException {
});
});
});

assertThat(openshiftList).filteredOn(h -> "Service".equals(h.getKind())).hasOnlyOneElementSatisfying(h -> {
assertThat(h).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getNamespace()).isNull();
});
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public void assertGeneratedResources() throws IOException {
assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).hasOnlyOneElementSatisfying(i -> {
assertThat(i).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getSpec()).satisfies(spec -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getNamespace()).isNull();
});

assertThat(spec.getTemplate()).satisfies(template -> {
assertThat(template.getSpec()).satisfies(templateSpec -> {
assertThat(templateSpec.getContainers()).hasSize(1).hasOnlyOneElementSatisfying(c -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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.List;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
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 KnativeWithApplicationPropertiesTest {

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(GreetingResource.class))
.setApplicationName("knative-with-application-properties")
.setApplicationVersion("0.1-SNAPSHOT")
.withConfigurationResource("knative-with-application.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())).hasOnlyOneElementSatisfying(i -> {
assertThat(i).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getSpec()).satisfies(spec -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("test-it");
assertThat(m.getLabels()).contains(entry("foo", "bar"));
assertThat(m.getAnnotations()).contains(entry("bar", "baz"));
assertThat(m.getNamespace()).isEqualTo("applications");
});

assertThat(spec.getTemplate()).satisfies(template -> {
assertThat(template.getSpec()).satisfies(templateSpec -> {
assertThat(templateSpec.getContainers()).hasSize(1).hasOnlyOneElementSatisfying(c -> {
assertThat(c.getPorts()).hasSize(1).hasOnlyOneElementSatisfying(p -> {
assertThat(p.getName()).isEqualTo("http1");
});
});
});
});
});
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void assertGeneratedResources() throws IOException {
assertThat(d.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("test-it");
assertThat(m.getLabels()).contains(entry("foo", "bar"));
assertThat(m.getNamespace()).isEqualTo("applications");
});

assertThat(d.getSpec()).satisfies(deploymentSpec -> {
Expand Down Expand Up @@ -82,6 +83,10 @@ public void assertGeneratedResources() throws IOException {

assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).hasOnlyOneElementSatisfying(i -> {
assertThat(i).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getNamespace()).isEqualTo("applications");
});

assertThat(s.getSpec()).satisfies(spec -> {
assertEquals("NodePort", spec.getType());
assertThat(spec.getPorts()).hasSize(1).hasOnlyOneElementSatisfying(p -> {
Expand All @@ -96,11 +101,12 @@ public void assertGeneratedResources() throws IOException {

assertThat(kubernetesList).filteredOn(i -> "Ingress".equals(i.getKind())).hasOnlyOneElementSatisfying(i -> {
assertThat(i).isInstanceOfSatisfying(Ingress.class, in -> {
//Check that lables and annotations are also applied to Ingresses (#10260)
//Check that labels and annotations are also applied to Ingresses (#10260)
assertThat(i.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("test-it");
assertThat(m.getLabels()).contains(entry("foo", "bar"));
assertThat(m.getAnnotations()).contains(entry("bar", "baz"));
assertThat(m.getNamespace()).isEqualTo("applications");
});

assertThat(in.getSpec().getRules()).hasOnlyOneElementSatisfying(r -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public void assertGeneratedResources() throws IOException {
assertThat(m.getName()).isEqualTo("minikube-with-application-properties");
assertThat(m.getLabels()).contains(entry("foo", "bar"));
assertThat(m.getAnnotations()).contains(entry("bar", "baz"));
assertThat(m.getNamespace()).isEqualTo("applications");
});

assertThat(d.getSpec()).satisfies(deploymentSpec -> {
Expand All @@ -64,6 +65,10 @@ public void assertGeneratedResources() throws IOException {

assertThat(kubernetesList).filteredOn(i -> "Service".equals(i.getKind())).hasOnlyOneElementSatisfying(i -> {
assertThat(i).isInstanceOfSatisfying(Service.class, s -> {
assertThat(s.getMetadata()).satisfies(m -> {
assertThat(m.getNamespace()).isEqualTo("applications");
});

assertThat(s.getSpec()).satisfies(spec -> {
assertEquals("NodePort", spec.getType());
assertThat(spec.getPorts()).hasSize(1).hasOnlyOneElementSatisfying(p -> {
Expand Down
Loading