Skip to content

Commit

Permalink
fix: use istag from other ns when internal reg is used
Browse files Browse the repository at this point in the history
  • Loading branch information
iocanel committed Aug 8, 2023
1 parent 1c73af2 commit 617ff8b
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 8 deletions.
6 changes: 6 additions & 0 deletions docs/src/main/asciidoc/deploying-to-openshift.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ To trigger a container image build:

The build that will be performed is a _s2i binary_ build.
The input of the build is the jar that has been built locally and the output of the build is an `ImageStream` that is configured to automatically trigger a deployment.
The base/builder image is specified using `base-jvm-image` and `base-native-image` for jvm and native mode respectively. An ImageStream for the image is automatically generated, unless these properties are used to reference an existing ImageStreamTag in the internal openshift registry. For example:

[source,properties]
----
quarkus.openshift.base-jvm-image=image-registry.openshift-image-registry.svc:5000/some-project/openjdk-11:17.1.16.
----

[NOTE]
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ public static String getDefaultJvmImage(CompiledJavaVersionBuildItem.JavaVersion

/**
* The base image to be used when a container image is being produced for the jar build.
*
* The value of this property is used to create an ImageStream for the builder image used in the Openshift build.
* When it references images already available in the internal Openshift registry, the corresponding streams are used
* instead.
* When the application is built against Java 17 or higher, {@code registry.access.redhat.com/ubi8/openjdk-17:1.16}
* is used as the default.
* Otherwise {@code registry.access.redhat.com/ubi8/openjdk-11:1.16} is used as the default.
Expand All @@ -54,7 +56,10 @@ public static String getDefaultJvmImage(CompiledJavaVersionBuildItem.JavaVersion
public Optional<String> baseJvmImage;

/**
* The base image to be used when a container image is being produced for the native binary build
* The base image to be used when a container image is being produced for the native binary build.
* The value of this property is used to create an ImageStream for the builder image used in the Openshift build.
* When it references images already available in the internal Openshift registry, the corresponding streams are used
* instead.
*/
@ConfigItem(defaultValue = DEFAULT_BASE_NATIVE_IMAGE)
public String baseNativeImage;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.quarkus.kubernetes.deployment;

import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT_INTERNAL_REGISTRY_PROJECT;

import java.util.Optional;

import io.dekorate.kubernetes.decorator.*;
import io.dekorate.s2i.decorator.AddBuildConfigResourceDecorator;
import io.dekorate.utils.Images;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.openshift.api.model.SourceBuildStrategyFluent;
import io.quarkus.container.spi.ImageReference;

public class ApplyBuilderImageDecorator extends NamedResourceDecorator<SourceBuildStrategyFluent<?>> {

Expand All @@ -21,11 +25,23 @@ public ApplyBuilderImageDecorator(String name, String image) {

@Override
public void andThenVisit(SourceBuildStrategyFluent<?> strategy, ObjectMeta meta) {
String builderRepository = Images.getRepository(image);
String builderTag = Images.getTag(image);
ImageReference imageRef = ImageReference.parse(image);

String builderRepository = imageRef.getRepository();
String builderTag = imageRef.getTag();
String builderName = !builderRepository.contains("/") ? builderRepository
: builderRepository.substring(builderRepository.lastIndexOf("/") + 1);
strategy.withNewFrom().withKind("ImageStreamTag").withName(builderName + ":" + builderTag).endFrom();
Optional<String> builderGroup = Optional.of(builderRepository)
.filter(s -> s.contains("/"))
.map(s -> s.substring(0, s.indexOf("/")));

boolean usesInternalRegistry = imageRef.getRegistry()
.filter(registry -> registry.contains(OPENSHIFT_INTERNAL_REGISTRY_PROJECT)).isPresent();
strategy.withNewFrom()
.withKind("ImageStreamTag")
.withName(builderName + ":" + builderTag)
.withNamespace(builderGroup.filter(g -> usesInternalRegistry).orElse(null))
.endFrom();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public final class Constants {
static final String DEFAULT_S2I_IMAGE_NAME = "s2i-java"; //refers to the Dekorate default image.

static final String OPENSHIFT_INTERNAL_REGISTRY = "image-registry.openshift-image-registry.svc:5000";
static final String OPENSHIFT_INTERNAL_REGISTRY_PROJECT = "openshift-image-registry"; //a more relaxed str to match

static final String KNATIVE = "knative";
static final String KNATIVE_SERVICE = "Service";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static io.quarkus.kubernetes.deployment.Constants.LIVENESS_PROBE;
import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT;
import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT_APP_RUNTIME;
import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT_INTERNAL_REGISTRY_PROJECT;
import static io.quarkus.kubernetes.deployment.Constants.QUARKUS;
import static io.quarkus.kubernetes.deployment.Constants.READINESS_PROBE;
import static io.quarkus.kubernetes.deployment.Constants.ROUTE;
Expand Down Expand Up @@ -43,6 +44,7 @@
import io.quarkus.container.spi.ContainerImageInfoBuildItem;
import io.quarkus.container.spi.ContainerImageLabelBuildItem;
import io.quarkus.container.spi.FallbackContainerImageRegistryBuildItem;
import io.quarkus.container.spi.ImageReference;
import io.quarkus.container.spi.SingleSegmentContainerImageRequestBuildItem;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
Expand Down Expand Up @@ -300,14 +302,26 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
// Handle custom s2i builder images
baseImage.map(BaseImageInfoBuildItem::getImage).ifPresent(builderImage -> {
String builderImageName = ImageUtil.getName(builderImage);
S2iBuildConfig s2iBuildConfig = new S2iBuildConfigBuilder().withBuilderImage(builderImage).build();
if (!DEFAULT_S2I_IMAGE_NAME.equals(builderImageName)) {
result.add(new DecoratorBuildItem(OPENSHIFT, new RemoveBuilderImageResourceDecorator(DEFAULT_S2I_IMAGE_NAME)));
}

if (containerImageConfig.builder.isEmpty() || config.isOpenshiftBuildEnabled(containerImageConfig, capabilities)) {
result.add(new DecoratorBuildItem(OPENSHIFT, new AddBuilderImageStreamResourceDecorator(s2iBuildConfig)));
result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyBuilderImageDecorator(name, builderImage)));
ImageReference imageRef = ImageReference.parse(builderImage);
boolean usesInternalRegistry = imageRef.getRegistry()
.filter(registry -> registry.contains(OPENSHIFT_INTERNAL_REGISTRY_PROJECT)).isPresent();
if (usesInternalRegistry) {
// When the internal registry is specified for the image, we assume the stream already exists
// It's better if we refer to it directly as (as an ImageStreamTag).
// In this case we need to remove the ImageStream (created by dekorate).
String repository = imageRef.getRepository();
String imageStreamName = repository.substring(repository.lastIndexOf("/"));
result.add(new DecoratorBuildItem(OPENSHIFT, new RemoveBuilderImageResourceDecorator(imageStreamName)));
} else {
S2iBuildConfig s2iBuildConfig = new S2iBuildConfigBuilder().withBuilderImage(builderImage).build();
result.add(new DecoratorBuildItem(OPENSHIFT, new AddBuilderImageStreamResourceDecorator(s2iBuildConfig)));
}
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.openshift.api.model.BuildConfig;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.DeploymentTriggerImageChangeParams;
import io.fabric8.openshift.api.model.ImageStream;
import io.quarkus.builder.Version;
import io.quarkus.maven.dependency.Dependency;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;

public class OpenshiftWithBaseImageFromInternalRegistryTest {

private static final String APP_NAME = "openshift-with-base-image-stream";

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class))
.setApplicationName(APP_NAME)
.setApplicationVersion("0.1-SNAPSHOT")
.overrideConfigKey("quarkus.openshift.base-jvm-image",
"image-registry.openshift-image-registry.svc:5000/myns/myimage:1.0")
.setForcedDependencies(List.of(Dependency.of("io.quarkus", "quarkus-openshift", 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("openshift.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("openshift.yml"));
List<HasMetadata> openshiftList = DeserializationUtil.deserializeAsList(kubernetesDir.resolve("openshift.yml"));

assertThat(openshiftList).filteredOn(h -> "DeploymentConfig".equals(h.getKind())).singleElement().satisfies(h -> {
assertThat(h.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo(APP_NAME);
});
assertThat(h).isInstanceOfSatisfying(DeploymentConfig.class, d -> {
Container container = d.getSpec().getTemplate().getSpec().getContainers().get(0);
assertThat(container.getImage()).endsWith(APP_NAME + ":0.1-SNAPSHOT");

DeploymentTriggerImageChangeParams imageTriggerParams = d.getSpec().getTriggers().get(0).getImageChangeParams();
assertThat(imageTriggerParams.getFrom().getKind()).isEqualTo("ImageStreamTag");
assertThat(imageTriggerParams.getFrom().getName()).isEqualTo(APP_NAME + ":0.1-SNAPSHOT");
});
});

assertThat(openshiftList).filteredOn(h -> "BuildConfig".equals(h.getKind())).singleElement().satisfies(h -> {
assertThat(h.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo(APP_NAME);
});
assertThat(h).isInstanceOfSatisfying(BuildConfig.class, b -> {
assertThat(b.getSpec().getStrategy().getSourceStrategy().getFrom()).satisfies(f -> {
assertThat(f.getKind()).isEqualTo("ImageStreamTag");
assertThat(f.getNamespace()).isEqualTo("myns");
assertThat(f.getName()).isEqualTo("myimage:1.0");
});
});
});

//Verify that we only got one Image
assertThat(openshiftList).filteredOn(h -> "ImageStream".equals(h.getKind())).singleElement()
.satisfies(h -> {
assertThat(h.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo(APP_NAME);
});

assertThat(h).isInstanceOfSatisfying(ImageStream.class, i -> {
assertThat(i.getSpec().getDockerImageRepository()).isNull();
});
});

}
}

0 comments on commit 617ff8b

Please sign in to comment.