Skip to content

Commit 5e44b81

Browse files
committed
Allow configuring the deploy strategy to perform K8s deployments
Fix #26789
1 parent d6e0af8 commit 5e44b81

File tree

17 files changed

+236
-87
lines changed

17 files changed

+236
-87
lines changed

extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftProcessor.java

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.quarkus.container.image.openshift.deployment;
22

3+
import static io.quarkus.container.image.openshift.deployment.OpenshiftUtils.getDeployStrategy;
34
import static io.quarkus.container.image.openshift.deployment.OpenshiftUtils.getNamespace;
45
import static io.quarkus.container.image.openshift.deployment.OpenshiftUtils.mergeConfig;
56
import static io.quarkus.container.util.PathsUtil.findMainSourcesRoot;
@@ -39,6 +40,9 @@
3940
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
4041
import io.fabric8.kubernetes.client.KubernetesClientException;
4142
import io.fabric8.kubernetes.client.dsl.LogWatch;
43+
import io.fabric8.kubernetes.client.dsl.NamespaceableResource;
44+
import io.fabric8.kubernetes.client.dsl.base.PatchContext;
45+
import io.fabric8.kubernetes.client.dsl.base.PatchType;
4246
import io.fabric8.kubernetes.client.http.HttpClient;
4347
import io.fabric8.openshift.api.model.Build;
4448
import io.fabric8.openshift.api.model.BuildConfig;
@@ -68,6 +72,7 @@
6872
import io.quarkus.kubernetes.client.deployment.KubernetesClientErrorHandler;
6973
import io.quarkus.kubernetes.client.spi.KubernetesClientBuildItem;
7074
import io.quarkus.kubernetes.spi.DecoratorBuildItem;
75+
import io.quarkus.kubernetes.spi.DeployStrategy;
7176
import io.quarkus.kubernetes.spi.KubernetesCommandBuildItem;
7277
import io.quarkus.kubernetes.spi.KubernetesEnvBuildItem;
7378

@@ -403,27 +408,7 @@ private static void applyOpenshiftResources(OpenShiftClient client, List<HasMeta
403408
// Apply build resource requirements
404409
try {
405410
for (HasMetadata i : distinct(buildResources)) {
406-
if (i instanceof BuildConfig) {
407-
client.resource(i).cascading(true).delete();
408-
try {
409-
client.resource(i).waitUntilCondition(d -> d == null, 10, TimeUnit.SECONDS);
410-
} catch (IllegalArgumentException e) {
411-
// We should ignore that, as its expected to be thrown when item is actually
412-
// deleted.
413-
}
414-
} else if (i instanceof ImageStream) {
415-
ImageStream is = (ImageStream) i;
416-
ImageStream existing = client.imageStreams().withName(i.getMetadata().getName()).get();
417-
if (existing != null &&
418-
existing.getSpec() != null &&
419-
existing.getSpec().getDockerImageRepository() != null &&
420-
existing.getSpec().getDockerImageRepository().equals(is.getSpec().getDockerImageRepository())) {
421-
LOG.info("Found: " + i.getKind() + " " + i.getMetadata().getName() + " repository: "
422-
+ existing.getSpec().getDockerImageRepository());
423-
continue;
424-
}
425-
}
426-
client.resource(i).createOrReplace();
411+
deployResource(client, i);
427412
LOG.info("Applied: " + i.getKind() + " " + i.getMetadata().getName());
428413
}
429414
OpenshiftUtils.waitForImageStreamTags(client, buildResources, 2, TimeUnit.MINUTES);
@@ -549,6 +534,55 @@ private static KubernetesClient buildClient(KubernetesClientBuildItem kubernetes
549534
return kubernetesClientBuilder.buildClient();
550535
}
551536

537+
private static void deployResource(OpenShiftClient client, HasMetadata metadata) {
538+
DeployStrategy deployStrategy = getDeployStrategy();
539+
var r = client.resource(metadata);
540+
// Delete build config it already existed unless the deploy strategy is not create or update.
541+
if (deployStrategy != DeployStrategy.CreateOrUpdate && r instanceof BuildConfig) {
542+
deleteBuildConfig(client, metadata, r);
543+
}
544+
545+
// If the image stream is already installed, we proceed with the next.
546+
if (r instanceof ImageStream) {
547+
ImageStream is = (ImageStream) r;
548+
ImageStream existing = client.imageStreams().withName(metadata.getMetadata().getName()).get();
549+
if (existing != null &&
550+
existing.getSpec() != null &&
551+
existing.getSpec().getDockerImageRepository() != null &&
552+
existing.getSpec().getDockerImageRepository().equals(is.getSpec().getDockerImageRepository())) {
553+
LOG.info("Found: " + metadata.getKind() + " " + metadata.getMetadata().getName() + " repository: "
554+
+ existing.getSpec().getDockerImageRepository());
555+
return;
556+
}
557+
}
558+
559+
// Deploy the current resource.
560+
switch (deployStrategy) {
561+
case Create:
562+
r.create();
563+
break;
564+
case Replace:
565+
r.replace();
566+
break;
567+
case ServerSideApply:
568+
r.patch(PatchContext.of(PatchType.SERVER_SIDE_APPLY));
569+
break;
570+
default:
571+
r.createOrReplace();
572+
break;
573+
}
574+
}
575+
576+
private static void deleteBuildConfig(OpenShiftClient client, HasMetadata metadata, NamespaceableResource<HasMetadata> r) {
577+
r.cascading(true).delete();
578+
try {
579+
client.resource(metadata).waitUntilCondition(d -> d == null, 10, TimeUnit.SECONDS);
580+
} catch (IllegalArgumentException e) {
581+
// We should ignore that, as its expected to be thrown when item is actually
582+
// deleted.
583+
}
584+
}
585+
552586
// visible for test
553587
static String concatUnixPaths(String... elements) {
554588
StringBuilder result = new StringBuilder();

extensions/container-image/container-image-openshift/deployment/src/main/java/io/quarkus/container/image/openshift/deployment/OpenshiftUtils.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.fabric8.openshift.api.model.ImageStreamTag;
2020
import io.fabric8.openshift.api.model.SourceBuildStrategyFluent;
2121
import io.fabric8.openshift.client.OpenShiftClient;
22+
import io.quarkus.kubernetes.spi.DeployStrategy;
2223

2324
/**
2425
* This class is copied from Dekorate, with the difference that the {@code waitForImageStreamTags} method
@@ -30,6 +31,8 @@ public class OpenshiftUtils {
3031

3132
private static final String OPENSHIFT_NAMESPACE = "quarkus.openshift.namespace";
3233
private static final String KUBERNETES_NAMESPACE = "quarkus.kubernetes.namespace";
34+
private static final String OPENSHIFT_DEPLOY_STRATEGY = "quarkus.openshift.deploy-strategy";
35+
private static final String KUBERNETES_DEPLOY_STRATEGY = "quarkus.kubernetes.deploy-strategy";
3336

3437
/**
3538
* Wait for the references ImageStreamTags to become available.
@@ -153,4 +156,13 @@ public static Optional<String> getNamespace() {
153156
return ConfigProvider.getConfig().getOptionalValue(OPENSHIFT_NAMESPACE, String.class)
154157
.or(() -> ConfigProvider.getConfig().getOptionalValue(KUBERNETES_NAMESPACE, String.class));
155158
}
159+
160+
/**
161+
* @return the openshift deploy strategy set in the OpenShift/Kubernetes extensions.
162+
*/
163+
public static DeployStrategy getDeployStrategy() {
164+
return ConfigProvider.getConfig().getOptionalValue(OPENSHIFT_DEPLOY_STRATEGY, DeployStrategy.class)
165+
.or(() -> ConfigProvider.getConfig().getOptionalValue(KUBERNETES_DEPLOY_STRATEGY, DeployStrategy.class))
166+
.orElse(DeployStrategy.CreateOrUpdate);
167+
}
156168
}

extensions/kubernetes/kind/deployment/src/main/java/io/quarkus/kind/deployment/KindProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void checkKind(ApplicationInfoBuildItem applicationInfo, KubernetesConfig
5757
BuildProducer<KubernetesResourceMetadataBuildItem> resourceMeta) {
5858
deploymentTargets.produce(
5959
new KubernetesDeploymentTargetBuildItem(KIND, DEPLOYMENT, DEPLOYMENT_GROUP, DEPLOYMENT_VERSION,
60-
KIND_PRIORITY, true));
60+
KIND_PRIORITY, true, config.getDeployStrategy()));
6161

6262
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
6363
resourceMeta.produce(

extensions/kubernetes/minikube/deployment/src/main/java/io/quarkus/minikube/deployment/MinikubeProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void checkMinikube(ApplicationInfoBuildItem applicationInfo, KubernetesCo
5454
BuildProducer<KubernetesResourceMetadataBuildItem> resourceMeta) {
5555
deploymentTargets.produce(
5656
new KubernetesDeploymentTargetBuildItem(MINIKUBE, DEPLOYMENT, DEPLOYMENT_GROUP, DEPLOYMENT_VERSION,
57-
MINIKUBE_PRIORITY, true));
57+
MINIKUBE_PRIORITY, true, config.getDeployStrategy()));
5858

5959
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
6060
resourceMeta.produce(

extensions/kubernetes/openshift/deployment/src/main/java/io/quarkus/openshift/deployment/OpenshiftProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ public void checkOpenshift(ApplicationInfoBuildItem applicationInfo, Capabilitie
2424
.produce(
2525
new KubernetesDeploymentTargetBuildItem(OPENSHIFT, deploymentResourceKind.kind,
2626
deploymentResourceKind.apiGroup,
27-
deploymentResourceKind.apiVersion, true));
27+
deploymentResourceKind.apiVersion, true,
28+
config.getDeployStrategy()));
2829

2930
String name = ResourceNameUtil.getResourceName(config, applicationInfo);
3031
resourceMeta.produce(new KubernetesResourceMetadataBuildItem(OPENSHIFT, deploymentResourceKind.apiGroup,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.quarkus.kubernetes.spi;
2+
3+
public enum DeployStrategy {
4+
/**
5+
* To create or replace the resources.
6+
*/
7+
CreateOrUpdate,
8+
/**
9+
* To create the resources if it does not exist. If resources already exist, it will fail.
10+
*/
11+
Create,
12+
/**
13+
* To update the existing resources in the target Kubernetes cluster. If no resources exist, it will fail.
14+
*/
15+
Replace,
16+
/**
17+
* To perform patch updates to the existing resources. If no resources exist, it will fail.
18+
* More information in <a href="https://kubernetes.io/docs/reference/using-api/server-side-apply/">server-side-apply</a>.
19+
*/
20+
ServerSideApply;
21+
}

extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/KubernetesDeploymentTargetBuildItem.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,27 @@ public final class KubernetesDeploymentTargetBuildItem extends MultiBuildItem
3535
private final String version;
3636
private final int priority;
3737
private final boolean enabled;
38+
private final DeployStrategy deployStrategy;
3839

39-
public KubernetesDeploymentTargetBuildItem(String name, String kind, String group, String version) {
40-
this(name, kind, group, version, DEFAULT_PRIORITY, false);
40+
public KubernetesDeploymentTargetBuildItem(String name, String kind, String group, String version,
41+
DeployStrategy deployStrategy) {
42+
this(name, kind, group, version, DEFAULT_PRIORITY, false, deployStrategy);
4143
}
4244

43-
public KubernetesDeploymentTargetBuildItem(String name, String kind, String group, String version, boolean enabled) {
44-
this(name, kind, group, version, DEFAULT_PRIORITY, enabled);
45+
public KubernetesDeploymentTargetBuildItem(String name, String kind, String group, String version, boolean enabled,
46+
DeployStrategy deployStrategy) {
47+
this(name, kind, group, version, DEFAULT_PRIORITY, enabled, deployStrategy);
4548
}
4649

4750
public KubernetesDeploymentTargetBuildItem(String name, String kind, String group, String version, int priority,
48-
boolean enabled) {
51+
boolean enabled, DeployStrategy deployStrategy) {
4952
this.name = Objects.requireNonNull(name, "'name' must not be null");
5053
this.kind = Objects.requireNonNull(kind, "'kind' must not be null");
5154
this.version = Objects.requireNonNull(version, "'version' must not be null");
5255
this.group = group;
5356
this.priority = priority;
5457
this.enabled = enabled;
58+
this.deployStrategy = deployStrategy;
5559
}
5660

5761
public String getGroup() {
@@ -78,6 +82,10 @@ public boolean isEnabled() {
7882
return enabled;
7983
}
8084

85+
public DeployStrategy getDeployStrategy() {
86+
return deployStrategy;
87+
}
88+
8189
public boolean nameAndKindMatch(KubernetesDeploymentTargetBuildItem other) {
8290
return this.name.equals(other.getName()) && this.kind.equals(other.getKind());
8391
}
@@ -103,7 +111,8 @@ private KubernetesDeploymentTargetBuildItem merge(KubernetesDeploymentTargetBuil
103111

104112
return new KubernetesDeploymentTargetBuildItem(this.name, this.kind, this.group, this.version,
105113
Math.max(this.priority, other.getPriority()),
106-
this.enabled || other.isEnabled());
114+
this.enabled || other.isEnabled(),
115+
this.deployStrategy);
107116
}
108117

109118
@Override
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.quarkus.kubernetes.spi;
22

3+
import static io.quarkus.kubernetes.spi.DeployStrategy.CreateOrUpdate;
34
import static org.assertj.core.api.Assertions.assertThat;
45

56
import java.util.Arrays;
@@ -12,21 +13,21 @@ class KubernetesDeploymentTargetBuildItemTest {
1213
@Test
1314
void testMergeList() {
1415
List<KubernetesDeploymentTargetBuildItem> input = Arrays.asList(
15-
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", 0, false),
16-
new KubernetesDeploymentTargetBuildItem("n2", "k2", "g1", "v1", 10, false),
17-
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", -10, false),
18-
new KubernetesDeploymentTargetBuildItem("n3", "k3", "g1", "v1", Integer.MIN_VALUE, false),
19-
new KubernetesDeploymentTargetBuildItem("n4", "k4", "g1", "v1", Integer.MIN_VALUE, true),
20-
new KubernetesDeploymentTargetBuildItem("n2", "k2", "g1", "v1", -10, true),
21-
new KubernetesDeploymentTargetBuildItem("n4", "k4", "g1", "v1", Integer.MAX_VALUE, true),
22-
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", 100, false));
16+
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", 0, false, CreateOrUpdate),
17+
new KubernetesDeploymentTargetBuildItem("n2", "k2", "g1", "v1", 10, false, CreateOrUpdate),
18+
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", -10, false, CreateOrUpdate),
19+
new KubernetesDeploymentTargetBuildItem("n3", "k3", "g1", "v1", Integer.MIN_VALUE, false, CreateOrUpdate),
20+
new KubernetesDeploymentTargetBuildItem("n4", "k4", "g1", "v1", Integer.MIN_VALUE, true, CreateOrUpdate),
21+
new KubernetesDeploymentTargetBuildItem("n2", "k2", "g1", "v1", -10, true, CreateOrUpdate),
22+
new KubernetesDeploymentTargetBuildItem("n4", "k4", "g1", "v1", Integer.MAX_VALUE, true, CreateOrUpdate),
23+
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", 100, false, CreateOrUpdate));
2324

2425
List<KubernetesDeploymentTargetBuildItem> result = KubernetesDeploymentTargetBuildItem.mergeList(input);
2526

2627
assertThat(result).containsOnly(
27-
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", 100, false),
28-
new KubernetesDeploymentTargetBuildItem("n2", "k2", "g1", "v1", 10, true),
29-
new KubernetesDeploymentTargetBuildItem("n3", "k3", "g1", "v1", Integer.MIN_VALUE, false),
30-
new KubernetesDeploymentTargetBuildItem("n4", "k4", "g1", "v1", Integer.MAX_VALUE, true));
28+
new KubernetesDeploymentTargetBuildItem("n1", "k1", "g1", "v1", 100, false, CreateOrUpdate),
29+
new KubernetesDeploymentTargetBuildItem("n2", "k2", "g1", "v1", 10, true, CreateOrUpdate),
30+
new KubernetesDeploymentTargetBuildItem("n3", "k3", "g1", "v1", Integer.MIN_VALUE, false, CreateOrUpdate),
31+
new KubernetesDeploymentTargetBuildItem("n4", "k4", "g1", "v1", Integer.MAX_VALUE, true, CreateOrUpdate));
3132
}
3233
}

extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/DeploymentTargetEntry.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package io.quarkus.kubernetes.deployment;
22

3+
import io.quarkus.kubernetes.spi.DeployStrategy;
4+
35
public class DeploymentTargetEntry {
46
private final String name;
57
private final String kind;
68
private final int priority;
9+
private final DeployStrategy deployStrategy;
710

8-
public DeploymentTargetEntry(String name, String kind, int priority) {
11+
public DeploymentTargetEntry(String name, String kind, int priority, DeployStrategy deployStrategy) {
912
this.name = name;
1013
this.kind = kind;
1114
this.priority = priority;
15+
this.deployStrategy = deployStrategy;
1216
}
1317

1418
public String getName() {
@@ -22,4 +26,8 @@ public String getKind() {
2226
public int getPriority() {
2327
return priority;
2428
}
29+
30+
public DeployStrategy getDeployStrategy() {
31+
return deployStrategy;
32+
}
2533
}

extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.dekorate.kubernetes.annotation.ImagePullPolicy;
99
import io.dekorate.kubernetes.annotation.ServiceType;
10+
import io.quarkus.kubernetes.spi.DeployStrategy;
1011
import io.quarkus.runtime.annotations.ConfigItem;
1112
import io.quarkus.runtime.annotations.ConfigRoot;
1213

@@ -498,6 +499,12 @@ public EnvVarsConfig getEnv() {
498499
@ConfigItem(defaultValue = "false")
499500
boolean deploy;
500501

502+
/**
503+
* If deploy is enabled, it will follow this strategy to update the resources to the target Knative cluster.
504+
*/
505+
@ConfigItem(defaultValue = "CreateOrUpdate")
506+
DeployStrategy deployStrategy;
507+
501508
public Optional<String> getAppSecret() {
502509
return this.appSecret;
503510
}

0 commit comments

Comments
 (0)