diff --git a/docs/src/main/asciidoc/kubernetes.adoc b/docs/src/main/asciidoc/kubernetes.adoc index b42f74deb539f..359705ca712f0 100644 --- a/docs/src/main/asciidoc/kubernetes.adoc +++ b/docs/src/main/asciidoc/kubernetes.adoc @@ -8,7 +8,7 @@ https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc include::./attributes.adoc[] This guide covers generating and deploying Kubernetes resources based on sane defaults and user supplied configuration. -In detail, it supports generating resources for <>, <> and <>. Also it supports automatically <> these resources to the target platform. +In detail, it supports generating resources for <>, <> and <. Also it supports automatically <> these resources to the target platform. == Prerequisites @@ -59,7 +59,7 @@ By using the following configuration for example: [source] ---- -kubernetes.group=yourDockerUsername # this is optional and defaults to your username if not set. +quarkus.kubernetes.group=yourDockerUsername # this is optional and defaults to your username if not set. quarkus.application.name=test-quarkus-app # this is also optional and defaults to the project name if not set ---- @@ -169,7 +169,7 @@ To change the number of replicas from 1 to 3: [source] ---- -kubernetes.replicas=3 +quarkus.kubernetes.replicas=3 ---- === Defining a docker registry and repository @@ -178,8 +178,8 @@ The docker registry and the user of the docker image can be specified, with the [source] ---- -kubernetes.group=myUser -docker.registry=http://my.docker-registry.net +quarkus.kubernetes.group=myUser +quarkus.docker.registry=http://my.docker-registry.net ---- Note: These options used to be `quarkus.kubernetes.docker.registry` and `quarkus.kubernetes.group` respectively. @@ -189,8 +189,7 @@ To add a new label to all generated resources, say `foo=bar`: [source] ---- -kubernetes.labels[0].key=foo -kubernetes.labels[0].value=bar +quarkus.kubernetes.labels.foo=bar ---- === Customizing the readiness probe: @@ -198,8 +197,8 @@ To set the initial delay of the probe to 20 seconds and the period to 45: [source] ---- -kubernetes.readiness-probe.initial-delay-seconds=20 -kubernetes.readiness-probe.period-seconds=45 +quarkus.kubernetes.readiness-probe.initial-delay-seconds=20 +quarkus.kubernetes.readiness-probe.period-seconds=45 ---- Here you can find a complete reference to all the available configuration options: @@ -210,38 +209,37 @@ The table below describe all the available configuration options. .Kubernetes |==== -| Property | Type | Description | Default Value -| kubernetes.group | String | | -| kubernetes.name | String | | -| kubernetes.version | String | | -| kubernetes.init-containers | Container[] | | -| kubernetes.labels | Label[] | | -| kubernetes.annotations | Annotation[] | | -| kubernetes.env-vars | Env[] | | -| kubernetes.working-dir | String | | -| kubernetes.command | String[] | | -| kubernetes.arguments | String[] | | -| kubernetes.replicas | int | | 1 -| kubernetes.service-account | String | | -| kubernetes.host | String | | -| kubernetes.ports | Port[] | | -| kubernetes.service-type | ServiceType | | ClusterIP -| kubernetes.pvc-volumes | PersistentVolumeClaimVolume[] | | -| kubernetes.secret-volumes | SecretVolume[] | | -| kubernetes.config-map-volumes | ConfigMapVolume[] | | -| kubernetes.git-repo-volumes | GitRepoVolume[] | | -| kubernetes.aws-elastic-block-store-volumes | AwsElasticBlockStoreVolume[] | | -| kubernetes.azure-disk-volumes | AzureDiskVolume[] | | -| kubernetes.azure-file-volumes | AzureFileVolume[] | | -| kubernetes.mounts | Mount[] | | -| kubernetes.image-pull-policy | ImagePullPolicy | | IfNotPresent -| kubernetes.image-pull-secrets | String[] | | -| kubernetes.liveness-probe | Probe | | ( see Probe ) -| kubernetes.readiness-probe | Probe | | ( see Probe ) -| kubernetes.sidecars | Container[] | | -| kubernetes.expose | boolean | | false -| kubernetes.headless | boolean | | false -| kubernetes.auto-deploy-enabled | boolean | | false +| Property | Type | Description | Default Value +| quarkus.kubernetes.group | String | | +| quarkus.kubernetes.name | String | | +| quarkus.kubernetes.version | String | | +| quarkus.kubernetes.init-containers | Map | | +| quarkus.kubernetes.labels | Map | | +| quarkus.kubernetes.annotations | Map | | +| quarkus.kubernetes.env-vars | Map | | +| quarkus.kubernetes.working-dir | String | | +| quarkus.kubernetes.command | String[] | | +| quarkus.kubernetes.arguments | String[] | | +| quarkus.kubernetes.replicas | int | | 1 +| quarkus.kubernetes.service-account | String | | +| quarkus.kubernetes.host | String | | +| quarkus.kubernetes.ports | Map | | +| quarkus.kubernetes.service-type | ServiceType | | ClusterIP +| quarkus.kubernetes.pvc-volumes | Map | | +| quarkus.kubernetes.secret-volumes | Map | | +| quarkus.kubernetes.config-map-volumes | Map | | +| quarkus.kubernetes.git-repo-volumes | Map | | +| quarkus.kubernetes.aws-elastic-block-store-volumes | Map | | +| quarkus.kubernetes.azure-disk-volumes | Map | | +| quarkus.kubernetes.azure-file-volumes | Map | | +| quarkus.kubernetes.mounts | Map | | +| quarkus.kubernetes.image-pull-policy | ImagePullPolicy | | IfNotPresent +| quarkus.kubernetes.image-pull-secrets | String[] | | +| quarkus.kubernetes.liveness-probe | Probe | | ( see Probe ) +| quarkus.kubernetes.readiness-probe | Probe | | ( see Probe ) +| quarkus.kubernetes.sidecars | Map | | +| quarkus.kubernetes.expose | boolean | | false +| quarkus.kubernetes.headless | boolean | | false |==== Properties that use non standard types, can be referenced by expanding the property. @@ -249,8 +247,8 @@ For example to define a `kubernetes-readiness-probe` which is of type `Probe`: [source] ---- -kubernetes.readiness-probe.initial-delay-seconds=20 -kubernetes.readiness-probe.period-seconds=45 +quarkus.kubernetes.readiness-probe.initial-delay-seconds=20 +quarkus.kubernetes.readiness-probe.period-seconds=45 ---- In this example `initial-delay` and `period-seconds` are fields of the type `Probe`. @@ -259,27 +257,10 @@ Below you will find tables describing all available types. ===== Basic Types -.Annotation -|==== -| Property | Type | Description | Default Value -| key | String | | -| value | String | | -|==== - - -.Label -|==== -| Property | Type | Description | Default Value -| key | String | | -| value | String | | -|==== - - .Env |==== | Property | Type | Description | Default Value -| name | String | | -| value | String | | +| value | String | | | secret | String | | | configmap | String | | | field | String | | @@ -299,7 +280,6 @@ Below you will find tables describing all available types. .Port |==== | Property | Type | Description | Default Value -| name | String | | | container-port | int | | | host-port | int | | 0 | path | String | | / @@ -310,8 +290,7 @@ Below you will find tables describing all available types. |==== | Property | Type | Description | Default Value | image | String | | -| name | String | | -| env-vars | Env[] | | +| env-vars | Env[] | | | working-dir | String | | | command | String[] | | | arguments | String[] | | @@ -328,7 +307,6 @@ Below you will find tables describing all available types. .Mount |==== | Property | Type | Description | Default Value -| name | String | | | path | String | | | sub-path | String | | | read-only | boolean | | false @@ -337,8 +315,7 @@ Below you will find tables describing all available types. .ConfigMapVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | -| config-map-name | String | | +| config-map-name | String | | | default-mode | int | | 384 | optional | boolean | | false |==== @@ -346,8 +323,7 @@ Below you will find tables describing all available types. .SecretVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | -| secret-name | String | | +| secret-name | String | | | default-mode | int | | 384 | optional | boolean | | false |==== @@ -356,8 +332,7 @@ Below you will find tables describing all available types. .AzureDiskVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | -| disk-name | String | | +| disk-name | String | | | disk-uri | String | | | kind | String | | Managed | caching-mode | String | | ReadWrite @@ -368,8 +343,7 @@ Below you will find tables describing all available types. .AwsElasticBlockStoreVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | -| volume-id | String | | +| volume-id | String | | | partition | int | | | fs-type | String | | ext4 | read-only | boolean | | false @@ -378,7 +352,6 @@ Below you will find tables describing all available types. .GitRepoVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | | repository | String | | | directory | String | | | revision | String | | @@ -387,112 +360,85 @@ Below you will find tables describing all available types. .PersistentVolumeClaimVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | -| claim-name | String | | +| claim-name | String | | | read-only | boolean | | false |==== .AzureFileVolume |==== | Property | Type | Description | Default Value -| volume-name | String | | -| share-name | String | | +| share-name | String | | | secret-name | String | | | read-only | boolean | | false |==== -== Docker - -.Docker -|==== -| Property | Type | Description | Default Value -| docker.docker-file | String | | Dockerfile -| docker.registry | String | | -| docker.auto-push-enabled | boolean | | false -| docker.auto-build-enabled | boolean | | false -|==== - === OpenShift To enable the generation of OpenShift resources, you need to include OpenShift in the target platforms: [source] ---- -kubernetes.deployment.target=openshift +quarkus.kubernetes.deployment-target=openshift ---- If you need to generate resources for both platforms (vanilla Kubernetes and OpenShift), then you need to include both (coma separated). [source] ---- -kubernetes.deployment.target=kubernetes, openshift +quarkus.kubernetes.deployment-target=kubernetes, openshift ---- The OpenShift resources can be customized in a similar approach with Kubernetes. .Openshift |==== -| Property | Type | Description | Default Value -| openshift.group | String | | -| openshift.name | String | | -| openshift.version | String | | -| openshift.init-containers | Container[] | | -| openshift.labels | Label[] | | -| openshift.annotations | Annotation[] | | -| openshift.env-vars | Env[] | | -| openshift.working-dir | String | | -| openshift.command | String[] | | -| openshift.arguments | String[] | | -| openshift.replicas | int | | 1 -| openshift.service-account | String | | -| openshift.host | String | | -| openshift.ports | Port[] | | -| openshift.service-type | ServiceType | | ClusterIP -| openshift.pvc-volumes | PersistentVolumeClaimVolume[] | | -| openshift.secret-volumes | SecretVolume[] | | -| openshift.config-map-volumes | ConfigMapVolume[] | | -| openshift.git-repo-volumes | GitRepoVolume[] | | -| openshift.aws-elastic-block-store-volumes | AwsElasticBlockStoreVolume[] | | -| openshift.azure-disk-volumes | AzureDiskVolume[] | | -| openshift.azure-file-volumes | AzureFileVolume[] | | -| openshift.mounts | Mount[] | | -| openshift.image-pull-policy | ImagePullPolicy | | IfNotPresent -| openshift.image-pull-secrets | String[] | | -| openshift.liveness-probe | Probe | | ( see Probe ) -| openshift.readiness-probe | Probe | | ( see Probe ) -| openshift.sidecars | Container[] | | -| openshift.expose | boolean | | false -| openshift.headless | boolean | | false -| openshift.auto-deploy-enabled | boolean | | false -|==== - -.S2i -|==== -| Property | Type | Description | Default Value -| s2i.enabled | boolean | | true -| s2i.docker-file | String | | Dockerfile -| s2i.registry | String | | -| s2i.builder-image | String | | fabric8/s2i-java:2.3 -| s2i.build-env-vars | Env[] | | -| s2i.auto-push-enabled | boolean | | false -| s2i.auto-build-enabled | boolean | | false -| s2i.auto-deploy-enabled | boolean | | false +| Property | Type | Description | Default Value +| quarkus.openshift.group | String | | +| quarkus.openshift.name | String | | +| quarkus.openshift.version | String | | +| quarkus.openshift.init-containers | Map | | +| quarkus.openshift.labels | Map | | +| quarkus.openshift.annotations | Map | | +| quarkus.openshift.env-vars | Map | | +| quarkus.openshift.working-dir | String | | +| quarkus.openshift.command | String[] | | +| quarkus.openshift.arguments | String[] | | +| quarkus.openshift.replicas | int | | 1 +| quarkus.openshift.service-account | String | | +| quarkus.openshift.host | String | | +| quarkus.openshift.ports | Map | | +| quarkus.openshift.service-type | ServiceType | | ClusterIP +| quarkus.openshift.pvc-volumes | Map | | +| quarkus.openshift.secret-volumes | Map | | +| quarkus.openshift.config-map-volumes | Map | | +| quarkus.openshift.git-repo-volumes | Map | | +| quarkus.openshift.aws-elastic-block-store-volumes | Map | | +| quarkus.openshift.azure-disk-volumes | Map | | +| quarkus.openshift.azure-file-volumes | Map | | +| quarkus.openshift.mounts | Map | | +| quarkus.openshift.image-pull-policy | ImagePullPolicy | | IfNotPresent +| quarkus.openshift.image-pull-secrets | String[] | | +| quarkus.openshift.liveness-probe | Probe | | ( see Probe ) +| quarkus.openshift.readiness-probe | Probe | | ( see Probe ) +| quarkus.openshift.sidecars | Map | | +| quarkus.openshift.expose | boolean | | false +| quarkus.openshift.headless | boolean | | false |==== === Knative -To enable the generation of Knative resources, you need to include Knative in the target platforms: +To enable the generation of Quarkus.Knative.resources, you need to include Knative in the target platforms: [source] ---- -kubernetes.deployment.target=knative +quarkus.kubernetes.deployment.target=knative ---- Following the execution of `./mvnw package` you will notice amongst the other files that are created, two files named -`knative.json` and `knative.yml` in the `target/kubernetes/` directory. +`quarkus.knative.json` and `knative.yml` in the `target/kubernetes/` directory. -If you look at either file you will see that it contains a Knative `Service`. +If you look at either file you will see that it contains a Quarkus.Knative.`Service`. -The full source of the `knative.json` file looks something like this: +The full source of the `quarkus.knative.json` file looks something like this: [source,json] ---- @@ -500,7 +446,7 @@ The full source of the `knative.json` file looks something like this: "apiVersion" : "v1", "kind" : "List", "items" : [ { - "apiVersion" : "serving.knative.dev/v1alpha1", + "apiVersion" : "serving.quarkus.knative.dev/v1alpha1", "kind" : "Service", "metadata" : { "labels" : { @@ -508,7 +454,7 @@ The full source of the `knative.json` file looks something like this: "version" : "0.1-SNAPSHOT", "group" : "yourDockerUsername" }, - "name" : "knative" + "name" : "quarkus.knative. }, "spec" : { "runLatest" : { @@ -532,36 +478,79 @@ The generated service can be customized using the following properties: .Knative |==== -| Property | Type | Description | Default Value -| knative.group | String | | -| knative.name | String | | -| knative.version | String | | -| knative.labels | Label[] | | -| knative.annotations | Annotation[] | | -| knative.env-vars | Env[] | | -| knative.working-dir | String | | -| knative.command | String[] | | -| knative.arguments | String[] | | -| knative.service-account | String | | -| knative.host | String | | -| knative.ports | Port[] | | -| knative.service-type | ServiceType | | ClusterIP -| knative.pvc-volumes | PersistentVolumeClaimVolume[] | | -| knative.secret-volumes | SecretVolume[] | | -| knative.config-map-volumes | ConfigMapVolume[] | | -| knative.git-repo-volumes | GitRepoVolume[] | | -| knative.aws-elastic-block-store-volumes | AwsElasticBlockStoreVolume[] | | -| knative.azure-disk-volumes | AzureDiskVolume[] | | -| knative.azure-file-volumes | AzureFileVolume[] | | -| knative.mounts | Mount[] | | -| knative.image-pull-policy | ImagePullPolicy | | IfNotPresent -| knative.image-pull-secrets | String[] | | -| knative.liveness-probe | Probe | | ( see Probe ) -| knative.readiness-probe | Probe | | ( see Probe ) -| knative.sidecars | Container[] | | -| knative.expose | boolean | | false +| Property | Type | Description | Default Value +| quarkus.knative.group | String | | +| quarkus.knative.name | String | | +| quarkus.knative.version | String | | +| quarkus.knative.init-containers | Map | | +| quarkus.knative.labels | Map | | +| quarkus.knative.annotations | Map | | +| quarkus.knative.env-vars | Map | | +| quarkus.knative.working-dir | String | | +| quarkus.knative.command | String[] | | +| quarkus.knative.arguments | String[] | | +| quarkus.knative.replicas | int | | 1 +| quarkus.knative.service-account | String | | +| quarkus.knative.host | String | | +| quarkus.knative.ports | Map | | +| quarkus.knative.service-type | ServiceType | | ClusterIP +| quarkus.knative.pvc-volumes | Map | | +| quarkus.knative.secret-volumes | Map | | +| quarkus.knative.config-map-volumes | Map | | +| quarkus.knative.git-repo-volumes | Map | | +| quarkus.knative.aws-elastic-block-store-volumes | Map | | +| quarkus.knative.azure-disk-volumes | Map | | +| quarkus.knative.azure-file-volumes | Map | | +| quarkus.knative.mounts | Map | | +| quarkus.knative.image-pull-policy | ImagePullPolicy | | IfNotPresent +| quarkus.knative.image-pull-secrets | String[] | | +| quarkus.knative.liveness-probe | Probe | | ( see Probe ) +| quarkus.knative.readiness-probe | Probe | | ( see Probe ) +| quarkus.knative.sidecars | Map | | |==== + +=== Deprecated configuration + +The following categories of configuration properties have been deprecated. + +==== Properties without the quarkus prefix + +In earlier versions of the extension, the `quarkus.` was missing from those properties. These properties are now deprecated. + +==== Docker and S2i properties + +The properties for configuring `docker` and `s2i` are also deprecated in favor of the new container-image extensions. + +==== Config group arrays + +Properties refering to config group arrays (e.g. kubernetes.labels[0], kubernetes.env-vars[0] etc) have been converted to maps, to align with the rest of the quarkus ecosystem. + +The code below demonstrates the change in `labels` config: + +[source] +---- +# Old labels config: +kubernetes.labels[0].name=foo +kubernetes.labels[0].name=bar + +# New labels +quarkus.kubernetes.labels.foo=bar +---- + +The code below demonstrates the change in `env-vars` config: + +[source] +---- +# Old env-vars config: +kubernetes.env-vars[0].name=foo +kubernetes.env-vars[0].configmap=my-configmap + +# New env-vars +quarkus.kubernetes.env-vars.foo.configmap=myconfigmap +---- + + == Deployment To trigger building and deploying a container image you need to enable the `quarkus.container.deploy` flag. @@ -584,7 +573,7 @@ Each time deployment is requested, a container build will be implicitly triggere === Deploying -When deployment is enabled, the kubernetes extension will selected the resources specified by `kubernetes.deployment.target` and deploy them. +When deployment is enabled, the kubernetes extension will selected the resources specified by `quarkus.kubernetes.deployment.target` and deploy them. This assumes that a `.kube/config` is available in your user directory that points to a real kubernetes cluster. In other words the extension will use whatever cluster `kubectl` uses. The same applies to credentials. diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AwsElasticBlockStoreVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AwsElasticBlockStoreVolumeConfig.java new file mode 100644 index 0000000000000..f90408593000e --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AwsElasticBlockStoreVolumeConfig.java @@ -0,0 +1,36 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class AwsElasticBlockStoreVolumeConfig { + + /** + * The name of the disk to mount. + */ + @ConfigItem + String volumeId; + + /** + * The partition. + */ + @ConfigItem + Optional partition; + + /** + * Filesystem type. + */ + @ConfigItem(defaultValue = "ext4") + String fsType; + + /** + * Wether the volumeName is read only or not. + */ + @ConfigItem(defaultValue = "false") + boolean readOnly; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AwsElasticBlockStoreVolumeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AwsElasticBlockStoreVolumeConverter.java new file mode 100644 index 0000000000000..87c192d2465fc --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AwsElasticBlockStoreVolumeConverter.java @@ -0,0 +1,23 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.AwsElasticBlockStoreVolume; +import io.dekorate.kubernetes.config.AwsElasticBlockStoreVolumeBuilder; + +public class AwsElasticBlockStoreVolumeConverter { + + public static AwsElasticBlockStoreVolume convert(Map.Entry e) { + return convert(e.getValue()).withVolumeName(e.getKey()).build(); + } + + private static AwsElasticBlockStoreVolumeBuilder convert(AwsElasticBlockStoreVolumeConfig c) { + AwsElasticBlockStoreVolumeBuilder b = new AwsElasticBlockStoreVolumeBuilder(); + b.withVolumeId(c.volumeId); + b.withFsType(c.fsType); + b.withReadOnly(c.readOnly); + c.partition.ifPresent(p -> b.withPartition(p)); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConfig.java new file mode 100644 index 0000000000000..7e94ffa7c8b0c --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConfig.java @@ -0,0 +1,57 @@ + +package io.quarkus.kubernetes.deployment; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class AzureDiskVolumeConfig { + + public enum CachingMode { + ReadWrite, + ReadOnly, + None + } + + public enum Kind { + Managed, + Shared + } + + /** + * The name of the disk to mount. + */ + @ConfigItem + String diskName; + + /** + * The URI of the vhd blob object OR the resourceID of an Azure managed data disk if Kind is Managed + */ + @ConfigItem + String diskURI; + + /** + * Kind of disk. + */ + @ConfigItem(defaultValue = "Managed") + Kind kind; + + /** + * Disk caching mode. + */ + @ConfigItem(defaultValue = "ReadWrite") + CachingMode cachingMode; + + /** + * File system type. + */ + @ConfigItem(defaultValue = "ext4") + String fsType; + + /** + * Wether the volumeName is read only or not. + */ + @ConfigItem(defaultValue = "false") + boolean readOnly; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java new file mode 100644 index 0000000000000..a48ebb1ed2395 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureDiskVolumeConverter.java @@ -0,0 +1,25 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.AzureDiskVolume; +import io.dekorate.kubernetes.config.AzureDiskVolumeBuilder; + +public class AzureDiskVolumeConverter { + + public static AzureDiskVolume convert(Map.Entry e) { + return convert(e.getValue()).withVolumeName(e.getKey()).build(); + } + + private static AzureDiskVolumeBuilder convert(AzureDiskVolumeConfig c) { + AzureDiskVolumeBuilder b = new AzureDiskVolumeBuilder(); + b.withNewDiskName(c.diskName); + b.withDiskURI(c.diskURI); + b.withKind(c.kind.name()); + b.withCachingMode(c.cachingMode.name()); + b.withFsType(c.fsType); + b.withReadOnly(c.readOnly); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureFileVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureFileVolumeConfig.java new file mode 100644 index 0000000000000..e371879b175ce --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureFileVolumeConfig.java @@ -0,0 +1,28 @@ + +package io.quarkus.kubernetes.deployment; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class AzureFileVolumeConfig { + + /** + * The share name. + */ + @ConfigItem + String shareName; + + /** + * The secret name. + */ + @ConfigItem + String secretName; + + /** + * Wether the volumeName is read only or not. + */ + @ConfigItem(defaultValue = "false") + boolean readOnly; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureFileVolumeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureFileVolumeConverter.java new file mode 100644 index 0000000000000..2bd0173589c1c --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/AzureFileVolumeConverter.java @@ -0,0 +1,22 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.AzureFileVolume; +import io.dekorate.kubernetes.config.AzureFileVolumeBuilder; + +public class AzureFileVolumeConverter { + + public static AzureFileVolume convert(Map.Entry e) { + return convert(e.getValue()).withVolumeName(e.getKey()).build(); + } + + private static AzureFileVolumeBuilder convert(AzureFileVolumeConfig c) { + AzureFileVolumeBuilder b = new AzureFileVolumeBuilder(); + b.withSecretName(c.secretName); + b.withShareName(c.shareName); + b.withReadOnly(c.readOnly); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ConfigMapVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ConfigMapVolumeConfig.java new file mode 100644 index 0000000000000..4de1b7360dfd6 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ConfigMapVolumeConfig.java @@ -0,0 +1,29 @@ +package io.quarkus.kubernetes.deployment; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class ConfigMapVolumeConfig { + + /** + * The name of the ConfigMap to mount. + */ + @ConfigItem + String configMapName; + + /** + * Default mode. + * + * @return The default mode. + */ + @ConfigItem(defaultValue = "0600") + Integer defaultMode; + + /** + * Optional + */ + @ConfigItem(defaultValue = "false") + boolean optional; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ConfigMapVolumeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ConfigMapVolumeConverter.java new file mode 100644 index 0000000000000..997212404d1b3 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ConfigMapVolumeConverter.java @@ -0,0 +1,23 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.ConfigMapVolume; +import io.dekorate.kubernetes.config.ConfigMapVolumeBuilder; + +public class ConfigMapVolumeConverter { + + public static ConfigMapVolume convert(Map.Entry e) { + return convert(e.getValue()).withVolumeName(e.getKey()).build(); + } + + public static ConfigMapVolumeBuilder convert(ConfigMapVolumeConfig cm) { + ConfigMapVolumeBuilder b = new ConfigMapVolumeBuilder(); + b.withConfigMapName(cm.configMapName); + b.withDefaultMode(cm.defaultMode); + b.withOptional(cm.optional); + return b; + } + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java index 2be32c9179775..5b45d201d68c7 100644 --- a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java @@ -3,6 +3,9 @@ public class Constants { static final String KUBERNETES = "kubernetes"; + static final String OPENSHIFT = "openshift"; + static final String KNATIVE = "knative"; + static final String DEPLOYMENT_TARGET = "kubernetes.deployment.target"; static final String DEPLOY = "quarkus.kubernetes.deploy"; } diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java new file mode 100644 index 0000000000000..077c1ed7566f8 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConfig.java @@ -0,0 +1,96 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.dekorate.kubernetes.annotation.ImagePullPolicy; +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class ContainerConfig { + + /** + * The container image. + */ + @ConfigItem + Optional image; + + /** + * Environment variables to add to all containers. + */ + @ConfigItem + Map envVars; + + /** + * Working directory. + */ + @ConfigItem + Optional workingDir; + + /** + * The commands + */ + @ConfigItem + Optional> command; + + /** + * The arguments + * + * @return The arguments. + */ + @ConfigItem + Optional> arguments; + + /** + * The service account. + */ + @ConfigItem + Optional serviceAccount; + + /** + * The host under which the application is going to be exposed. + * + */ + @ConfigItem + Optional host; + + /** + * The application ports. + */ + @ConfigItem + Map ports; + + /** + * Image pull policy. + */ + @ConfigItem(defaultValue = "IfNotPresent") + ImagePullPolicy imagePullPolicy; + + /** + * The image pull secret + */ + @ConfigItem + Optional> imagePullSecrets; + + /** + * The liveness probe. + */ + @ConfigItem + Optional livenessProbe; + + /** + * The readiness probe. + */ + @ConfigItem + Optional readinessProbe; + + /** + * Volume mounts. + */ + @ConfigItem + Map mounts; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConverter.java new file mode 100644 index 0000000000000..29017b7883367 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ContainerConverter.java @@ -0,0 +1,26 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.Container; +import io.dekorate.kubernetes.config.ContainerBuilder; + +public class ContainerConverter { + + public static Container convert(Map.Entry e) { + return convert(e.getValue()).withName(e.getKey()).build(); + } + + private static ContainerBuilder convert(ContainerConfig c) { + ContainerBuilder b = new ContainerBuilder(); + c.image.ifPresent(i -> b.withImage(i)); + c.workingDir.ifPresent(w -> b.withWorkingDir(w)); + c.readinessProbe.ifPresent(p -> b.withReadinessProbe(ProbeConverter.convert(p))); + c.livenessProbe.ifPresent(p -> b.withLivenessProbe(ProbeConverter.convert(p))); + c.envVars.entrySet().forEach(e -> b.addToEnvVars(EnvConverter.convert(e))); + c.ports.entrySet().forEach(e -> b.addToPorts(PortConverter.convert(e))); + c.mounts.entrySet().forEach(e -> b.addToMounts(MountConverter.convert(e))); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvConfig.java new file mode 100644 index 0000000000000..9263e688c9581 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvConfig.java @@ -0,0 +1,41 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class EnvConfig { + + /** + * The environment variable name. + */ + @ConfigItem + Optional name; + + /** + * The environment variable value. + */ + @ConfigItem + Optional value; + + /** + * The environment variable secret. + */ + @ConfigItem + Optional secret; + + /** + * The environment variable config map. + */ + @ConfigItem + Optional configmap; + + /** + * The environment variable field. + */ + @ConfigItem + Optional field; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvConverter.java new file mode 100644 index 0000000000000..2538da6a9784e --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvConverter.java @@ -0,0 +1,25 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; +import java.util.regex.Pattern; + +import io.dekorate.kubernetes.config.Env; +import io.dekorate.kubernetes.config.EnvBuilder; + +public class EnvConverter { + + public static Env convert(Map.Entry e) { + return convert(e.getValue()).withName(e.getKey().toUpperCase().replaceAll(Pattern.quote("-"), "_")).build(); + } + + private static EnvBuilder convert(EnvConfig env) { + EnvBuilder b = new EnvBuilder(); + env.name.ifPresent(v -> b.withName(v)); + env.value.ifPresent(v -> b.withValue(v)); + env.secret.ifPresent(v -> b.withSecret(v)); + env.configmap.ifPresent(v -> b.withConfigmap(v)); + env.field.ifPresent(v -> b.withField(v)); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/GitRepoVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/GitRepoVolumeConfig.java new file mode 100644 index 0000000000000..cc1663a01617f --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/GitRepoVolumeConfig.java @@ -0,0 +1,30 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class GitRepoVolumeConfig { + + /** + * Git repoistory URL. + */ + @ConfigItem + String repository; + + /** + * The directory of the repository to mount. + */ + @ConfigItem + Optional directory; + + /** + * The commit hash to use. + */ + @ConfigItem + Optional revision; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java new file mode 100644 index 0000000000000..f22dd4ba34654 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java @@ -0,0 +1,288 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.dekorate.kubernetes.annotation.ImagePullPolicy; +import io.dekorate.kubernetes.annotation.ServiceType; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot +public class KnativeConfig implements PlatformConfiguration { + + /** + * The group of the application. + */ + @ConfigItem + Optional group; + + /** + * The name of the application. This value will be used for naming Kubernetes + * resources like: - Deployment - Service and so on ... + */ + @ConfigItem + Optional name; + + /** + * The version of the application. + */ + @ConfigItem + Optional version; + + /** + * Custom labels to add to all resources + */ + @ConfigItem + Map labels; + + /** + * Custom annotations to add to all resources + */ + @ConfigItem + Map annotations; + + /** + * Environment variables to add to all containers + */ + @ConfigItem + Map envVars; + + /** + * Working directory + */ + @ConfigItem + Optional workingDir; + + /** + * The commands + */ + @ConfigItem + Optional> command; + + /** + * The arguments + * + * @return The arguments + */ + @ConfigItem + Optional> arguments; + + /** + * The service account + */ + @ConfigItem + Optional serviceAccount; + + /** + * The host under which the application is going to be exposed + */ + @ConfigItem + Optional host; + + /** + * The application ports + */ + @ConfigItem + Map ports; + + /** + * The type of service that will be generated for the application + */ + @ConfigItem(defaultValue = "ClusterIP") + ServiceType serviceType; + + /** + * Image pull policy + */ + @ConfigItem(defaultValue = "IfNotPresent") + ImagePullPolicy imagePullPolicy; + + /** + * The image pull secret + */ + @ConfigItem + Optional> imagePullSecrets; + + /** + * The liveness probe + */ + @ConfigItem + Optional livenessProbe; + + /** + * The readiness probe + */ + @ConfigItem + Optional readinessProbe; + + /** + * Volume mounts + */ + @ConfigItem + Map mounts; + + /** + * Secret volumes + */ + @ConfigItem + Map secretVolumes; + + /** + * ConfigMap volumes + */ + @ConfigItem + Map configMapVolumes; + + /** + * Git Repository volumes + */ + @ConfigItem + Map gitRepoVolumes; + + /** + * Persistent Volume Claim volumes + */ + @ConfigItem + Map pvcVolumes; + + /** + * AWS Elastic BlockStore volumes + */ + @ConfigItem + Map awsElasticBlockStoreVolumes; + + /** + * Azure file volumes + */ + @ConfigItem + Map azureFileVolumes; + + /** + * Azure disk volumes + */ + @ConfigItem + Map azureDiskVolumes; + + /** + * Init containers + */ + @ConfigItem + Map initContainers; + + /** + * Sidecar containers + */ + @ConfigItem + Map containers; + + public Optional getGroup() { + return group; + } + + public Optional getName() { + return name; + } + + public Optional getVersion() { + return version; + } + + public Map getLabels() { + return labels; + } + + public Map getAnnotations() { + return annotations; + } + + public Map getEnvVars() { + return envVars; + } + + public Optional getWorkingDir() { + return workingDir; + } + + public Optional> getCommand() { + return command; + } + + public Optional> getArguments() { + return arguments; + } + + public Optional getServiceAccount() { + return serviceAccount; + } + + public Optional getHost() { + return host; + } + + public Map getPorts() { + return ports; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public ImagePullPolicy getImagePullPolicy() { + return imagePullPolicy; + } + + public Optional> getImagePullSecrets() { + return imagePullSecrets; + } + + public Optional getLivenessProbe() { + return livenessProbe; + } + + public Optional getReadinessProbe() { + return readinessProbe; + } + + public Map getMounts() { + return mounts; + } + + public Map getSecretVolumes() { + return secretVolumes; + } + + public Map getConfigMapVolumes() { + return configMapVolumes; + } + + public Map getGitRepoVolumes() { + return gitRepoVolumes; + } + + public Map getPvcVolumes() { + return pvcVolumes; + } + + public Map getAwsElasticBlockStoreVolumes() { + return awsElasticBlockStoreVolumes; + } + + public Map getAzureFileVolumes() { + return azureFileVolumes; + } + + public Map getAzureDiskVolumes() { + return azureDiskVolumes; + } + + public Map getInitContainers() { + return initContainers; + } + + public Map getContainers() { + return containers; + } + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java index 7ceefccf7e84c..557db4c30c96a 100644 --- a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java @@ -1,22 +1,295 @@ - package io.quarkus.kubernetes.deployment; import java.util.List; +import java.util.Map; +import java.util.Optional; +import io.dekorate.kubernetes.annotation.ImagePullPolicy; +import io.dekorate.kubernetes.annotation.ServiceType; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigRoot; @ConfigRoot -public class KubernetesConfig { +public class KubernetesConfig implements PlatformConfiguration { + + /** + * The group of the application. + */ + @ConfigItem + Optional group; + + /** + * The name of the application. This value will be used for naming Kubernetes + * resources like: - Deployment - Service and so on ... + */ + @ConfigItem + Optional name; + + /** + * The version of the application. + */ + @ConfigItem + Optional version; + + /** + * Custom labels to add to all resources + */ + @ConfigItem + Map labels; + + /** + * Custom annotations to add to all resources + */ + @ConfigItem + Map annotations; + + /** + * Environment variables to add to all containers + */ + @ConfigItem + Map envVars; + + /** + * Working directory + */ + @ConfigItem + Optional workingDir; + + /** + * The commands + */ + @ConfigItem + Optional> command; + + /** + * The arguments + * + * @return The arguments + */ + @ConfigItem + Optional> arguments; + + /** + * The service account + */ + @ConfigItem + Optional serviceAccount; + + /** + * The host under which the application is going to be exposed + */ + @ConfigItem + Optional host; /** - * The target deployment platform. - * Defaults to kubernetes. Can be kubernetes, openshift, knative or any combination of the above as comma separated list. + * The application ports + */ + @ConfigItem + Map ports; + + /** + * The type of service that will be generated for the application + */ + @ConfigItem(defaultValue = "ClusterIP") + ServiceType serviceType; + + /** + * Image pull policy + */ + @ConfigItem(defaultValue = "IfNotPresent") + ImagePullPolicy imagePullPolicy; + + /** + * The image pull secret + */ + @ConfigItem + Optional> imagePullSecrets; + + /** + * The liveness probe + */ + @ConfigItem + Optional livenessProbe; + + /** + * The readiness probe + */ + @ConfigItem + Optional readinessProbe; + + /** + * Volume mounts + */ + @ConfigItem + Map mounts; + + /** + * Secret volumes + */ + @ConfigItem + Map secretVolumes; + + /** + * ConfigMap volumes + */ + @ConfigItem + Map configMapVolumes; + + /** + * Git Repository volumes + */ + @ConfigItem + Map gitRepoVolumes; + + /** + * Persistent Volume Claim volumes + */ + @ConfigItem + Map pvcVolumes; + + /** + * AWS Elastic BlockStore volumes + */ + @ConfigItem + Map awsElasticBlockStoreVolumes; + + /** + * Azure file volumes + */ + @ConfigItem + Map azureFileVolumes; + + /** + * Azure disk volumes + */ + @ConfigItem + Map azureDiskVolumes; + + /** + * Init containers + */ + @ConfigItem + Map initContainers; + + /** + * Sidecar containers + */ + @ConfigItem + Map containers; + + /** + * The target deployment platform. Defaults to kubernetes. Can be kubernetes, + * openshift, knative or any combination of the above as comma separated list. */ @ConfigItem(defaultValue = "kubernetes") List deploymentTarget; - public List getDeploymentTarget() { - return this.deploymentTarget; + public Optional getGroup() { + return group; + } + + public Optional getName() { + return name; + } + + public Optional getVersion() { + return version; + } + + public Map getLabels() { + return labels; + } + + public Map getAnnotations() { + return annotations; + } + + public Map getEnvVars() { + return envVars; + } + + public Optional getWorkingDir() { + return workingDir; + } + + public Optional> getCommand() { + return command; + } + + public Optional> getArguments() { + return arguments; + } + + public Optional getServiceAccount() { + return serviceAccount; + } + + public Optional getHost() { + return host; } + + public Map getPorts() { + return ports; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public ImagePullPolicy getImagePullPolicy() { + return imagePullPolicy; + } + + public Optional> getImagePullSecrets() { + return imagePullSecrets; + } + + public Optional getLivenessProbe() { + return livenessProbe; + } + + public Optional getReadinessProbe() { + return readinessProbe; + } + + public Map getMounts() { + return mounts; + } + + public Map getSecretVolumes() { + return secretVolumes; + } + + public Map getConfigMapVolumes() { + return configMapVolumes; + } + + public Map getGitRepoVolumes() { + return gitRepoVolumes; + } + + public Map getPvcVolumes() { + return pvcVolumes; + } + + public Map getAwsElasticBlockStoreVolumes() { + return awsElasticBlockStoreVolumes; + } + + public Map getAzureFileVolumes() { + return azureFileVolumes; + } + + public Map getAzureDiskVolumes() { + return azureDiskVolumes; + } + + public Map getInitContainers() { + return initContainers; + } + + public Map getContainers() { + return containers; + } + } diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfigUtil.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfigUtil.java new file mode 100644 index 0000000000000..e654627e6df26 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfigUtil.java @@ -0,0 +1,117 @@ +package io.quarkus.kubernetes.deployment; + +import static io.quarkus.kubernetes.deployment.Constants.*; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; + +import io.dekorate.utils.Strings; + +public class KubernetesConfigUtil { + + private static final String DEKORATE_PREFIX = "dekorate."; + private static final String QUARKUS_PREFIX = "quarkus."; + + //Most of quarkus prefixed properties are handled directly by the config items (KubenretesConfig, OpenshiftConfig, KnativeConfig) + //We just need group, name & version parsed here, as we don't have decorators for these (low level properties). + private static final Set QUARKUS_PREFIX_WHITELIST = new HashSet<>(Arrays.asList("group", "name", "version")); + + private static final Set ALLOWED_GENERATORS = new HashSet<>( + Arrays.asList("kubernetes", "openshift", "knative", "docker", "s2i")); + private static final Set IMAGE_GENERATORS = new HashSet<>(Arrays.asList("docker", "s2i")); + + public static List getDeploymentTargets() { + return getDeploymentTargets(toMap()); + } + + public static List getDeploymentTargets(Map map) { + return Arrays.stream(map.getOrDefault(DEKORATE_PREFIX + DEPLOYMENT_TARGET, KUBERNETES).toString().split(",")) + .map(String::trim) + .map(String::toLowerCase) + .collect(Collectors.toList()); + } + + public static Optional getDockerRegistry(Map map) { + return IMAGE_GENERATORS.stream().map(g -> map.get(DEKORATE_PREFIX + g + ".registry")).filter(Objects::nonNull) + .map(String::valueOf).findFirst(); + } + + public static Optional getGroup(Map map) { + return ALLOWED_GENERATORS.stream().map(g -> map.get(DEKORATE_PREFIX + g + ".group")).filter(Objects::nonNull) + .map(String::valueOf).findFirst(); + } + + public static Optional getName(Map map) { + return ALLOWED_GENERATORS.stream().map(g -> map.get(DEKORATE_PREFIX + g + ".name")).filter(Objects::nonNull) + .map(String::valueOf).findFirst(); + } + + /* + * Collects configuration properties for Kubernetes. Reads all properties and + * matches properties that match known Dekorate generators. These properties may + * or may not be prefixed with `quarkus.` though the prefixed ones take + * precedence. + * + * @return A map containing the properties. + */ + public static Map toMap() { + Config config = ConfigProvider.getConfig(); + Map result = new HashMap<>(); + + Map quarkusPrefixed = StreamSupport.stream(config.getPropertyNames().spliterator(), false) + .filter(s -> s.startsWith(QUARKUS_PREFIX)) + .map(s -> s.replaceFirst(QUARKUS_PREFIX, "")) + .filter(k -> ALLOWED_GENERATORS.contains(generatorName(k))) + .filter(k -> QUARKUS_PREFIX_WHITELIST.contains(propertyName(k))) + .filter(k -> config.getOptionalValue(QUARKUS_PREFIX + k, String.class).isPresent()) + .collect(Collectors.toMap(k -> DEKORATE_PREFIX + k, + k -> config.getValue(QUARKUS_PREFIX + k, String.class))); + + Map unPrefixed = StreamSupport.stream(config.getPropertyNames().spliterator(), false) + .filter(k -> ALLOWED_GENERATORS.contains(generatorName(k))) + .filter(k -> config.getOptionalValue(k, String.class).isPresent()) + .collect(Collectors.toMap(k -> DEKORATE_PREFIX + k, k -> config.getValue(k, String.class))); + + result.putAll(unPrefixed); + result.putAll(quarkusPrefixed); + return result; + } + + /** + * Returns the name of the generators that can handle the specified key. + * + * @param key The key. + * @return The generator name or null if the key format is unexpected. + */ + private static String generatorName(String key) { + if (Strings.isNullOrEmpty(key) || !key.contains(".")) { + return null; + } + return key.substring(0, key.indexOf(".")); + } + + /** + * Returns the name of the property stripped of all prefixes. + * + * @param key The key. + * @return The property name. + */ + private static String propertyName(String key) { + if (Strings.isNullOrEmpty(key) || !key.contains(".")) { + return key; + } + return key.substring(key.lastIndexOf(".") + 1); + } + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java index 1653632ad3237..f8898ff24a726 100644 --- a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java @@ -1,23 +1,19 @@ package io.quarkus.kubernetes.deployment; -import static io.quarkus.kubernetes.deployment.Constants.DEPLOYMENT_TARGET; -import static io.quarkus.kubernetes.deployment.Constants.KUBERNETES; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; +import java.util.HashSet; +import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import javax.net.ssl.SSLHandshakeException; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import io.dekorate.deps.kubernetes.api.model.HasMetadata; @@ -40,7 +36,8 @@ public class KubernetesDeployer { private static final Logger LOG = Logger.getLogger(KubernetesDeployer.class); @BuildStep(onlyIf = { IsNormal.class, KubernetesDeploy.class }) - public void deploy(KubernetesClientBuildItem kubernetesClient, + public void deploy(KubernetesConfig kubernetesConfig, + KubernetesClientBuildItem kubernetesClient, ApplicationInfoBuildItem applicationInfo, Optional containerImage, OutputTargetBuildItem outputTarget, @@ -51,10 +48,14 @@ public void deploy(KubernetesClientBuildItem kubernetesClient, "A Kubernetes deployment was requested but no extension was found to build a container image. Consider adding one of following extensions: \"quarkus-container-image-jib\", \"quarkus-container-image-docker\" or \"quarkus-container-image-s2i\"."); } - Config config = ConfigProvider.getConfig(); - List deploymentTargets = Arrays - .stream(config.getOptionalValue(DEPLOYMENT_TARGET, String.class).orElse(KUBERNETES).split(",")) - .map(String::trim).map(String::toUpperCase).map(DeploymentTarget::valueOf).collect(Collectors.toList()); + Map config = KubernetesConfigUtil.toMap(); + Set deploymentTargets = new HashSet<>(); + deploymentTargets.addAll(KubernetesConfigUtil.getDeploymentTargets(config).stream() + .map(String::toUpperCase) + .map(DeploymentTarget::valueOf) + .collect(Collectors.toList())); + + deploymentTargets.addAll(kubernetesConfig.deploymentTarget); final KubernetesClient client = Clients.fromConfig(kubernetesClient.getClient().getConfiguration()); DeploymentTarget target = deploymentTargets.stream().findFirst().orElse(DeploymentTarget.KUBERNETES); diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java index a6bd62b2e16a1..47027f1fe8c2a 100644 --- a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java @@ -1,6 +1,9 @@ package io.quarkus.kubernetes.deployment; -import static io.quarkus.kubernetes.deployment.Constants.*; +import static io.quarkus.kubernetes.deployment.Constants.DEPLOY; +import static io.quarkus.kubernetes.deployment.Constants.KNATIVE; +import static io.quarkus.kubernetes.deployment.Constants.KUBERNETES; +import static io.quarkus.kubernetes.deployment.Constants.OPENSHIFT; import java.io.File; import java.io.IOException; @@ -8,7 +11,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -16,26 +18,38 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; import io.dekorate.Session; import io.dekorate.SessionWriter; +import io.dekorate.kubernetes.config.Annotation; import io.dekorate.kubernetes.config.EnvBuilder; +import io.dekorate.kubernetes.config.Label; import io.dekorate.kubernetes.config.PortBuilder; import io.dekorate.kubernetes.config.ProbeBuilder; import io.dekorate.kubernetes.configurator.AddPort; +import io.dekorate.kubernetes.decorator.AddAnnotationDecorator; +import io.dekorate.kubernetes.decorator.AddAwsElasticBlockStoreVolumeDecorator; +import io.dekorate.kubernetes.decorator.AddAzureDiskVolumeDecorator; +import io.dekorate.kubernetes.decorator.AddAzureFileVolumeDecorator; +import io.dekorate.kubernetes.decorator.AddConfigMapVolumeDecorator; import io.dekorate.kubernetes.decorator.AddEnvVarDecorator; +import io.dekorate.kubernetes.decorator.AddImagePullSecretDecorator; +import io.dekorate.kubernetes.decorator.AddInitContainerDecorator; +import io.dekorate.kubernetes.decorator.AddLabelDecorator; import io.dekorate.kubernetes.decorator.AddLivenessProbeDecorator; +import io.dekorate.kubernetes.decorator.AddMountDecorator; +import io.dekorate.kubernetes.decorator.AddPvcVolumeDecorator; import io.dekorate.kubernetes.decorator.AddReadinessProbeDecorator; import io.dekorate.kubernetes.decorator.AddRoleBindingResourceDecorator; +import io.dekorate.kubernetes.decorator.AddSecretVolumeDecorator; import io.dekorate.kubernetes.decorator.AddServiceAccountResourceDecorator; +import io.dekorate.kubernetes.decorator.AddSidecarDecorator; 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.ApplyServiceAccountNamedDecorator; +import io.dekorate.kubernetes.decorator.ApplyWorkingDirDecorator; import io.dekorate.processor.SimpleFileWriter; import io.dekorate.project.BuildInfo; import io.dekorate.project.FileProjectFactory; @@ -44,7 +58,6 @@ import io.dekorate.s2i.config.S2iBuildConfigBuilder; import io.dekorate.s2i.decorator.AddBuilderImageStreamResourceDecorator; import io.dekorate.utils.Maps; -import io.dekorate.utils.Strings; import io.quarkus.container.spi.BaseImageInfoBuildItem; import io.quarkus.container.spi.ContainerImageInfoBuildItem; import io.quarkus.deployment.IsNormal; @@ -66,13 +79,8 @@ class KubernetesProcessor { private static final String PROPERTY_PREFIX = "dekorate."; - private static final Set ALLOWED_GENERATORS = new HashSet( - Arrays.asList("kubernetes", "openshift", "knative", "docker", "s2i")); - private static final Set IMAGE_GENERATORS = new HashSet(Arrays.asList("docker", "s2i")); - private static final String DOCKER_REGISTRY_PROPERTY = PROPERTY_PREFIX + "docker.registry"; private static final String APP_GROUP_PROPERTY = "app.group"; - private static final String OUTPUT_ARTIFACT_FORMAT = "%s%s.jar"; @BuildStep(onlyIf = IsNormal.class) @@ -80,6 +88,9 @@ public void build(ApplicationInfoBuildItem applicationInfo, ArchiveRootBuildItem archiveRootBuildItem, OutputTargetBuildItem outputTargetBuildItem, PackageConfig packageConfig, + KubernetesConfig kubernetesConfig, + OpenshiftConfig openshiftConfig, + KnativeConfig knativeConfig, List kubernetesEnvBuildItems, List kubernetesRoleBuildItems, List kubernetesPortBuildItems, @@ -104,35 +115,22 @@ public void build(ApplicationInfoBuildItem applicationInfo, throw new RuntimeException("Unable to setup environment for generating Kubernetes resources", e); } - Config config = ConfigProvider.getConfig(); - List deploymentTargets = Arrays - .stream(config.getOptionalValue(DEPLOYMENT_TARGET, String.class) - .orElse(KUBERNETES).split(",")) - .map(String::trim) - .map(String::toLowerCase) - .collect(Collectors.toList()); - - Map configAsMap = StreamSupport.stream(config.getPropertyNames().spliterator(), false) - .filter(k -> ALLOWED_GENERATORS.contains(generatorName(k))) - .collect(Collectors.toMap(k -> PROPERTY_PREFIX + k, k -> config.getValue(k, String.class))); + Map config = KubernetesConfigUtil.toMap(); + Set deploymentTargets = new HashSet<>(); + deploymentTargets.addAll(KubernetesConfigUtil.getDeploymentTargets(config)); + deploymentTargets.addAll(kubernetesConfig.deploymentTarget.stream().map(Enum::name).map(String::toLowerCase) + .collect(Collectors.toList())); // this is a hack to get kubernetes.registry working because currently it's not supported as is in Dekorate - Optional dockerRegistry = IMAGE_GENERATORS.stream() - .map(g -> config.getOptionalValue(g + ".registry", String.class)) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); - + Optional dockerRegistry = KubernetesConfigUtil.getDockerRegistry(config); dockerRegistry.ifPresent(v -> System.setProperty(DOCKER_REGISTRY_PROPERTY, v)); // this is a hack to work around Dekorate using the default group for some of the properties - Optional kubernetesGroup = ALLOWED_GENERATORS.stream() - .map(g -> config.getOptionalValue(g + ".group", String.class)) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); + Optional kubernetesGroup = KubernetesConfigUtil.getGroup(config); kubernetesGroup.ifPresent(v -> System.setProperty(APP_GROUP_PROPERTY, v)); + String name = KubernetesConfigUtil.getName(config).orElse(applicationInfo.getName()); + Path artifactPath = archiveRootBuildItem.getArchiveRoot().resolve( String.format(OUTPUT_ARTIFACT_FORMAT, outputTargetBuildItem.getBaseName(), packageConfig.runnerSuffix)); final Map generatedResourcesMap; @@ -143,12 +141,18 @@ public void build(ApplicationInfoBuildItem applicationInfo, final Session session = Session.getSession(); session.setWriter(sessionWriter); - session.feed(Maps.fromProperties(configAsMap)); + session.feed(Maps.fromProperties(config)); + + //Apply configuration + applyGlobalConfig(session, kubernetesConfig); + applyConfig(session, KUBERNETES, name, kubernetesConfig); + applyConfig(session, OPENSHIFT, name, openshiftConfig); + applyConfig(session, KNATIVE, name, knativeConfig); //apply build item configurations to the dekorate session. applyBuildItems(session, + name, deploymentTargets, - applicationInfo, kubernetesEnvBuildItems, kubernetesRoleBuildItems, kubernetesPortBuildItems, @@ -195,9 +199,116 @@ public void build(ApplicationInfoBuildItem applicationInfo, featureProducer.produce(new FeatureBuildItem(FeatureBuildItem.KUBERNETES)); } + /** + * Apply global changes + * + * @param session The session to apply the changes + * @param config The {@link KubernetesConfig} instance + */ + private void applyGlobalConfig(Session session, KubernetesConfig config) { + //Ports + config.ports.entrySet().forEach(e -> session.configurators().add(new AddPort(PortConverter.convert(e)))); + } + + /** + * Apply changes to the target resource group + * + * @param session The session to apply the changes + * @param target The deployment target (e.g. kubernetes, openshift, knative) + * @param name The name of the resource to accept the configuration + * @param config The {@link PlatformConfiguration} instance + */ + private void applyConfig(Session session, String target, String name, PlatformConfiguration config) { + //Labels + config.getLabels().forEach((key, value) -> { + session.resources().decorate(target, new AddLabelDecorator(new Label(key, value))); + }); + + //Annotations + config.getAnnotations().forEach((key, value) -> { + session.resources().decorate(target, new AddAnnotationDecorator(new Annotation(key, value))); + }); + + //EnvVars + config.getEnvVars().entrySet().forEach(e -> { + session.resources().decorate(target, new AddEnvVarDecorator(EnvConverter.convert(e))); + }); + + config.getWorkingDir().ifPresent(w -> { + session.resources().decorate(target, new ApplyWorkingDirDecorator(name, DEPLOY)); + }); + + config.getCommand().ifPresent(c -> { + session.resources().decorate(target, + new ApplyCommandDecorator(name, c.toArray(new String[c.size()]))); + }); + + config.getArguments().ifPresent(a -> { + session.resources().decorate(target, new ApplyArgsDecorator(name, a.toArray(new String[a.size()]))); + }); + + config.getServiceAccount().ifPresent(s -> { + session.resources().decorate(target, new ApplyServiceAccountNamedDecorator(name, s)); + }); + + //Image Pull + session.resources().decorate(new ApplyImagePullPolicyDecorator(config.getImagePullPolicy())); + config.getImagePullSecrets().ifPresent(l -> { + l.forEach(s -> session.resources().decorate(target, new AddImagePullSecretDecorator(name, s))); + }); + + //Probes + config.getLivenessProbe().ifPresent(p -> { + session.resources().decorate(target, new AddLivenessProbeDecorator(name, ProbeConverter.convert(p))); + }); + + config.getReadinessProbe().ifPresent(p -> { + session.resources().decorate(target, + new AddReadinessProbeDecorator(name, ProbeConverter.convert(p))); + }); + + // Mounts and Volumes + config.getMounts().entrySet().forEach(e -> { + session.resources().decorate(target, new AddMountDecorator(MountConverter.convert(e))); + }); + + config.getSecretVolumes().entrySet().forEach(e -> { + session.resources().decorate(target, new AddSecretVolumeDecorator(SecretVolumeConverter.convert(e))); + }); + + config.getConfigMapVolumes().entrySet().forEach(e -> { + session.resources().decorate(target, new AddConfigMapVolumeDecorator(ConfigMapVolumeConverter.convert(e))); + }); + + config.getPvcVolumes().entrySet().forEach(e -> { + session.resources().decorate(target, new AddPvcVolumeDecorator(PvcVolumeConverter.convert(e))); + }); + + config.getAwsElasticBlockStoreVolumes().entrySet().forEach(e -> { + session.resources().decorate(target, + new AddAwsElasticBlockStoreVolumeDecorator(AwsElasticBlockStoreVolumeConverter.convert(e))); + }); + + config.getAzureFileVolumes().entrySet().forEach(e -> { + session.resources().decorate(target, new AddAzureFileVolumeDecorator(AzureFileVolumeConverter.convert(e))); + }); + + config.getAzureDiskVolumes().entrySet().forEach(e -> { + session.resources().decorate(target, new AddAzureDiskVolumeDecorator(AzureDiskVolumeConverter.convert(e))); + }); + + config.getInitContainers().entrySet().forEach(e -> { + session.resources().decorate(target, new AddInitContainerDecorator(name, ContainerConverter.convert(e))); + }); + + config.getContainers().entrySet().forEach(e -> { + session.resources().decorate(target, new AddSidecarDecorator(name, ContainerConverter.convert(e))); + }); + } + private void applyBuildItems(Session session, - List deploymentTargets, - ApplicationInfoBuildItem applicationInfo, + String name, + Set deploymentTargets, List kubernetesEnvBuildItems, List kubernetesRoleBuildItems, List kubernetesPortBuildItems, @@ -208,7 +319,7 @@ private void applyBuildItems(Session session, Optional kubernetesHealthReadinessPathBuildItem) { containerImageBuildItem.ifPresent(c -> session.resources() - .decorate(new ApplyImageDecorator(applicationInfo.getName(), c.getImage()))); + .decorate(new ApplyImageDecorator(name, c.getImage()))); //Handle env variables kubernetesEnvBuildItems.forEach(e -> session.resources() @@ -216,8 +327,8 @@ private void applyBuildItems(Session session, //Handle Command and arguments commandBuildItem.ifPresent(c -> { - session.resources().decorate(new ApplyCommandDecorator(applicationInfo.getName(), new String[] { c.getCommand() })); - session.resources().decorate(new ApplyArgsDecorator(applicationInfo.getName(), c.getArgs())); + session.resources().decorate(new ApplyCommandDecorator(name, new String[] { c.getCommand() })); + session.resources().decorate(new ApplyArgsDecorator(name, c.getArgs())); }); //Handle ports @@ -241,19 +352,19 @@ private void applyBuildItems(Session session, session.resources().decorate(DeploymentTarget.OPENSHIFT.name().toLowerCase(), new AddBuilderImageStreamResourceDecorator(s2iBuildConfig)); - session.resources().decorate(new ApplyBuilderImageDecorator(applicationInfo.getName(), builderImage)); + session.resources().decorate(new ApplyBuilderImageDecorator(name, builderImage)); }); } //Handle probes kubernetesHealthLivenessPathBuildItem .ifPresent(l -> session.resources() - .decorate(new AddLivenessProbeDecorator(applicationInfo.getName(), new ProbeBuilder() + .decorate(new AddLivenessProbeDecorator(name, new ProbeBuilder() .withHttpActionPath(l.getPath()) .build()))); kubernetesHealthReadinessPathBuildItem .ifPresent(r -> session.resources() - .decorate(new AddReadinessProbeDecorator(applicationInfo.getName(), new ProbeBuilder() + .decorate(new AddReadinessProbeDecorator(name, new ProbeBuilder() .withHttpActionPath(r.getPath()) .build()))); } @@ -290,17 +401,4 @@ private Project createProject(ApplicationInfoBuildItem app, Path artifactPath) { return new Project(project.getRoot(), buildInfo, project.getScmInfo()); } - /** - * Returns the name of the generators that can handle the specified key. - * - * @param key The key. - * @return The generator name or null if the key format is unexpected. - */ - private static String generatorName(String key) { - if (Strings.isNullOrEmpty(key) || !key.contains(".")) { - return null; - } - return key.substring(0, key.indexOf(".")); - } - } diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/MountConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/MountConfig.java new file mode 100644 index 0000000000000..e1f5878d88f8c --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/MountConfig.java @@ -0,0 +1,44 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class MountConfig { + + /** + * The name of the volumeName to mount. + * + * @return The name. + */ + @ConfigItem + Optional name; + + /** + * The path to mount. + * + * @return The path. + */ + @ConfigItem + Optional path; + + /** + * Path within the volumeName from which the container's volumeName should be + * mounted. + * + * @return The subPath. + */ + @ConfigItem + Optional subPath; + + /** + * ReadOnly + * + * @return True if mount is readonly, False otherwise. + */ + @ConfigItem(defaultValue = "false") + boolean readOnly; +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/MountConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/MountConverter.java new file mode 100644 index 0000000000000..32cff3efcaa22 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/MountConverter.java @@ -0,0 +1,22 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.Mount; +import io.dekorate.kubernetes.config.MountBuilder; + +public class MountConverter { + + public static Mount convert(Map.Entry e) { + return convert(e.getValue()).withName(e.getKey()).build(); + } + + private static MountBuilder convert(MountConfig mount) { + MountBuilder b = new MountBuilder(); + mount.path.ifPresent(v -> b.withPath(v)); + mount.subPath.ifPresent(v -> b.withSubPath(v)); + b.withReadOnly(mount.readOnly); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java new file mode 100644 index 0000000000000..167ddc3bf47ff --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java @@ -0,0 +1,289 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.dekorate.kubernetes.annotation.ImagePullPolicy; +import io.dekorate.kubernetes.annotation.ServiceType; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot +public class OpenshiftConfig implements PlatformConfiguration { + + /** + * The group of the application. + */ + @ConfigItem + Optional group; + + /** + * The name of the application. This value will be used for naming Kubernetes + * resources like: - Deployment - Service and so on ... + */ + @ConfigItem + Optional name; + + /** + * The version of the application. + */ + @ConfigItem + Optional version; + + /** + * Custom labels to add to all resources + */ + @ConfigItem + Map labels; + + /** + * Custom annotations to add to all resources + */ + @ConfigItem + Map annotations; + + /** + * Environment variables to add to all containers + */ + @ConfigItem + Map envVars; + + /** + * Working directory + */ + @ConfigItem + Optional workingDir; + + /** + * The commands + */ + @ConfigItem + Optional> command; + + /** + * The arguments + * + * @return The arguments + */ + @ConfigItem + Optional> arguments; + + /** + * The service account + */ + @ConfigItem + Optional serviceAccount; + + /** + * The host under which the application is going to be exposed + */ + @ConfigItem + Optional host; + + /** + * The application ports + */ + @ConfigItem + Map ports; + + /** + * The type of service that will be generated for the application + */ + @ConfigItem(defaultValue = "ClusterIP") + ServiceType serviceType; + + /** + * Image pull policy + */ + @ConfigItem(defaultValue = "IfNotPresent") + ImagePullPolicy imagePullPolicy; + + /** + * The image pull secret + */ + @ConfigItem + Optional> imagePullSecrets; + + /** + * The liveness probe + */ + @ConfigItem + Optional livenessProbe; + + /** + * The readiness probe + */ + @ConfigItem + Optional readinessProbe; + + /** + * Volume mounts + */ + @ConfigItem + Map mounts; + + /** + * Secret volumes + */ + @ConfigItem + Map secretVolumes; + + /** + * ConfigMap volumes + */ + @ConfigItem + Map configMapVolumes; + + /** + * Git Repository volumes + */ + @ConfigItem + Map gitRepoVolumes; + + /** + * Persistent Volume Claim volumes + */ + @ConfigItem + Map pvcVolumes; + + /** + * AWS Elastic BlockStore volumes + */ + @ConfigItem + Map awsElasticBlockStoreVolumes; + + /** + * Azure file volumes + */ + @ConfigItem + Map azureFileVolumes; + + /** + * Azure disk volumes + */ + @ConfigItem + Map azureDiskVolumes; + + /** + * Init containers + */ + @ConfigItem + Map initContainers; + + /** + * Sidecar containers + */ + @ConfigItem + Map containers; + + public Optional getGroup() { + return group; + } + + public Optional getName() { + return name; + } + + public Optional getVersion() { + return version; + } + + public Map getLabels() { + return labels; + } + + public Map getAnnotations() { + return annotations; + } + + public Map getEnvVars() { + return envVars; + } + + public Optional getWorkingDir() { + return workingDir; + } + + public Optional> getCommand() { + return command; + } + + public Optional> getArguments() { + return arguments; + } + + public Optional getServiceAccount() { + return serviceAccount; + } + + public Optional getHost() { + return host; + } + + public Map getPorts() { + return ports; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public ImagePullPolicy getImagePullPolicy() { + return imagePullPolicy; + } + + public Optional> getImagePullSecrets() { + return imagePullSecrets; + } + + public Optional getLivenessProbe() { + return livenessProbe; + } + + public Optional getReadinessProbe() { + return readinessProbe; + } + + public Map getMounts() { + return mounts; + } + + public Map getSecretVolumes() { + return secretVolumes; + } + + public Map getConfigMapVolumes() { + return configMapVolumes; + } + + public Map getGitRepoVolumes() { + return gitRepoVolumes; + } + + public Map getPvcVolumes() { + return pvcVolumes; + } + + public Map getAwsElasticBlockStoreVolumes() { + return awsElasticBlockStoreVolumes; + } + + public Map getAzureFileVolumes() { + return azureFileVolumes; + } + + public Map getAzureDiskVolumes() { + return azureDiskVolumes; + } + + public Map getInitContainers() { + return initContainers; + } + + public Map getContainers() { + return containers; + } + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java new file mode 100644 index 0000000000000..be4bcb1706a22 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PlatformConfiguration.java @@ -0,0 +1,67 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.dekorate.kubernetes.annotation.ImagePullPolicy; +import io.dekorate.kubernetes.annotation.ServiceType; + +public interface PlatformConfiguration { + + Optional getGroup(); + + Optional getName(); + + Optional getVersion(); + + Map getLabels(); + + Map getAnnotations(); + + Map getEnvVars(); + + Optional getWorkingDir(); + + Optional> getCommand(); + + Optional> getArguments(); + + Optional getServiceAccount(); + + Optional getHost(); + + Map getPorts(); + + ServiceType getServiceType(); + + ImagePullPolicy getImagePullPolicy(); + + Optional> getImagePullSecrets(); + + Optional getLivenessProbe(); + + Optional getReadinessProbe(); + + Map getMounts(); + + Map getSecretVolumes(); + + Map getConfigMapVolumes(); + + Map getGitRepoVolumes(); + + Map getPvcVolumes(); + + Map getAwsElasticBlockStoreVolumes(); + + Map getAzureFileVolumes(); + + Map getAzureDiskVolumes(); + + Map getInitContainers(); + + Map getContainers(); + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PortConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PortConfig.java new file mode 100644 index 0000000000000..73f70fff5e32c --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PortConfig.java @@ -0,0 +1,39 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Optional; + +import io.dekorate.kubernetes.annotation.Protocol; +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class PortConfig { + + /** + * The port number. Refers to the container port. + */ + @ConfigItem + Optional containerPort; + + /** + * The host port. + */ + @ConfigItem + Optional hostPort; + + /** + * The application path (refers to web application path). + * + * @return The path, defaults to /. + */ + @ConfigItem(defaultValue = "/") + Optional path; + + /** + * The protocol. + */ + @ConfigItem(defaultValue = "TCP") + Protocol protocol; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PortConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PortConverter.java new file mode 100644 index 0000000000000..4faf8ce1efdae --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PortConverter.java @@ -0,0 +1,22 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.Port; +import io.dekorate.kubernetes.config.PortBuilder; + +public class PortConverter { + + public static Port convert(Map.Entry e) { + return convert(e.getValue()).withName(e.getKey()).build(); + } + + private static PortBuilder convert(PortConfig port) { + PortBuilder b = new PortBuilder(); + port.path.ifPresent(v -> b.withPath(v)); + port.hostPort.ifPresent(v -> b.withHostPort(v)); + port.containerPort.ifPresent(v -> b.withContainerPort(v)); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java new file mode 100644 index 0000000000000..b3e4dc7617964 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConfig.java @@ -0,0 +1,66 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class ProbeConfig { + + /** + * The http path to use for the probe For this to work, the container port also + * needs to be set + * + * Assuming the container port has been set (as per above comment), if + * execAction or tcpSocketAction are not set, an http probe will be used + * automatically even if no path is set (which will result in the root path + * being used) + */ + @ConfigItem + Optional httpActionPath; + + /** + * The command to use for the probe. + */ + @ConfigItem + Optional execAction; + + /** + * The tcp socket to use for the probe (the format is host:port). + */ + @ConfigItem + Optional tcpSocketAction; + + /** + * The amount of time to wait in seconds before starting to probe. + */ + @ConfigItem(defaultValue = "0") + Integer initialDelaySeconds; + + /** + * The period in which the action should be called. + */ + @ConfigItem(defaultValue = "30") + Integer periodSeconds; + + /** + * The amount of time to wait for each action. + */ + @ConfigItem(defaultValue = "10") + Integer timeoutSeconds; + + /** + * The success threshold to use. + */ + @ConfigItem(defaultValue = "1") + Integer successThreshold; + + /** + * The failure threshold to use. + */ + @ConfigItem(defaultValue = "3") + Integer failureThreshold; + +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java new file mode 100644 index 0000000000000..d081210f2678b --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/ProbeConverter.java @@ -0,0 +1,21 @@ + +package io.quarkus.kubernetes.deployment; + +import io.dekorate.kubernetes.config.Probe; +import io.dekorate.kubernetes.config.ProbeBuilder; + +public class ProbeConverter { + + public static Probe convert(ProbeConfig probe) { + ProbeBuilder b = new ProbeBuilder(); + probe.httpActionPath.ifPresent(v -> b.withHttpActionPath(v)); + probe.execAction.ifPresent(v -> b.withExecAction(v)); + probe.tcpSocketAction.ifPresent(v -> b.withTcpSocketAction(v)); + b.withInitialDelaySeconds(probe.initialDelaySeconds); + b.withPeriodSeconds(probe.periodSeconds); + b.withTimeoutSeconds(probe.timeoutSeconds); + b.withSuccessThreshold(probe.successThreshold); + b.withFailureThreshold(probe.failureThreshold); + return b.build(); + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PvcVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PvcVolumeConfig.java new file mode 100644 index 0000000000000..0155325707ac6 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PvcVolumeConfig.java @@ -0,0 +1,29 @@ + +package io.quarkus.kubernetes.deployment; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class PvcVolumeConfig { + + /** + * The name of the claim to mount. + */ + @ConfigItem + String claimName; + + /** + * Default mode. + * + * @return The default mode. + */ + @ConfigItem(defaultValue = "0600") + Integer defaultMode; + + /** + * Optional + */ + @ConfigItem(defaultValue = "false") + boolean optional; +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PvcVolumeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PvcVolumeConverter.java new file mode 100644 index 0000000000000..da41eed2ed3b2 --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/PvcVolumeConverter.java @@ -0,0 +1,21 @@ + +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.PersistentVolumeClaimVolume; +import io.dekorate.kubernetes.config.PersistentVolumeClaimVolumeBuilder; + +public class PvcVolumeConverter { + + public static PersistentVolumeClaimVolume convert(Map.Entry e) { + return convert(e.getValue()).withVolumeName(e.getKey()).build(); + } + + public static PersistentVolumeClaimVolumeBuilder convert(PvcVolumeConfig c) { + PersistentVolumeClaimVolumeBuilder b = new PersistentVolumeClaimVolumeBuilder(); + b.withClaimName(c.claimName); + b.withReadOnly(c.optional); + return b; + } +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecretVolumeConfig.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecretVolumeConfig.java new file mode 100644 index 0000000000000..d128e293a0e5a --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecretVolumeConfig.java @@ -0,0 +1,25 @@ +package io.quarkus.kubernetes.deployment; + +import io.quarkus.runtime.annotations.ConfigItem; + +public class SecretVolumeConfig { + + /** + * The name of the secret to mount. + */ + String secretName; + + /** + * Default mode. + * + * @return The default mode. + */ + @ConfigItem(defaultValue = "0600") + Integer defaultMode; + + /** + * Optional + */ + @ConfigItem(defaultValue = "false") + boolean optional; +} diff --git a/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecretVolumeConverter.java b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecretVolumeConverter.java new file mode 100644 index 0000000000000..9a5e7fab803ce --- /dev/null +++ b/extensions/kubernetes/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecretVolumeConverter.java @@ -0,0 +1,21 @@ +package io.quarkus.kubernetes.deployment; + +import java.util.Map; + +import io.dekorate.kubernetes.config.SecretVolume; +import io.dekorate.kubernetes.config.SecretVolumeBuilder; + +public class SecretVolumeConverter { + + public static SecretVolume convert(Map.Entry e) { + return convert(e.getValue()).withVolumeName(e.getKey()).build(); + } + + public static SecretVolumeBuilder convert(SecretVolumeConfig c) { + SecretVolumeBuilder b = new SecretVolumeBuilder(); + b.withSecretName(c.secretName); + b.withDefaultMode(c.defaultMode); + b.withOptional(c.optional); + return b; + } +} diff --git a/integration-tests/kubernetes/invoker/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties b/integration-tests/kubernetes/invoker/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties index 06aab08e2c369..cdd463e8dca45 100644 --- a/integration-tests/kubernetes/invoker/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties +++ b/integration-tests/kubernetes/invoker/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties @@ -1,4 +1,4 @@ # Configuration file # key = value -kubernetes.deployment.target=openshift +quarkus.kubernetes.deployment-target=openshift quarkus.kubernetes-client.trust-certs=true diff --git a/integration-tests/kubernetes/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties b/integration-tests/kubernetes/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties index 06aab08e2c369..3d0cdee0c4c10 100644 --- a/integration-tests/kubernetes/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties +++ b/integration-tests/kubernetes/src/it/openshift-s2i-build-and-deploy/src/main/resources/application.properties @@ -1,4 +1,4 @@ # Configuration file # key = value -kubernetes.deployment.target=openshift +quarkus.kubernetes.deployment.target=openshift quarkus.kubernetes-client.trust-certs=true diff --git a/integration-tests/kubernetes/standard/src/test/resources/knative.properties b/integration-tests/kubernetes/standard/src/test/resources/knative.properties index a5c9db4bc1a0f..64bd7cf2283ff 100644 --- a/integration-tests/kubernetes/standard/src/test/resources/knative.properties +++ b/integration-tests/kubernetes/standard/src/test/resources/knative.properties @@ -1,2 +1,2 @@ # Configuration file -kubernetes.deployment.target=knative \ No newline at end of file +quarkus.kubernetes.deployment-target=knative diff --git a/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-application.properties b/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-application.properties index afe5aca7881b2..9f4ba363bb6cd 100644 --- a/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-application.properties +++ b/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-application.properties @@ -1,8 +1,6 @@ quarkus.http.port=9090 -kubernetes.name=test-it -kubernetes.labels[0].key=foo -kubernetes.labels[0].value=bar -kubernetes.env-vars[0].name=MY_ENV_VAR -kubernetes.env-vars[0].value=SOMEVALUE -kubernetes.group=grp -docker.registry=quay.io \ No newline at end of file +quarkus.kubernetes.name=test-it +quarkus.kubernetes.labels.foo=bar +quarkus.kubernetes.env-vars.my-env-var.value=SOMEVALUE +quarkus.container-image.group=grp +quarkus.container-image.registry=quay.io diff --git a/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-quarkus-app-name.properties b/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-quarkus-app-name.properties index ada7e29dcd271..1b8332079406f 100644 --- a/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-quarkus-app-name.properties +++ b/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-quarkus-app-name.properties @@ -1,11 +1,11 @@ quarkus.application.name=test -kubernetes.deployment.target=kubernetes,openshift -kubernetes.name=foo -kubernetes.group=bar -kubernetes.version=1.0-kube +quarkus.kubernetes.deployment-target=kubernetes,openshift +quarkus.kubernetes.name=foo +quarkus.kubernetes.group=bar +quarkus.kubernetes.version=1.0-kube -openshift.name=ofoo -openshift.group=obar -openshift.version=1.0-openshift +quarkus.openshift.name=ofoo +quarkus.openshift.group=obar +quarkus.openshift.version=1.0-openshift -s2i.name=s2ifoo +quarkus.s2i.name=s2ifoo diff --git a/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-sys-group.properties b/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-sys-group.properties index 24d056be8af0d..d56967af3aa40 100644 --- a/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-sys-group.properties +++ b/integration-tests/kubernetes/standard/src/test/resources/kubernetes-with-sys-group.properties @@ -1 +1 @@ -kubernetes.group=grp \ No newline at end of file +quarkus.kubernetes.group=grp diff --git a/integration-tests/kubernetes/standard/src/test/resources/openshift-with-application.properties b/integration-tests/kubernetes/standard/src/test/resources/openshift-with-application.properties index de5dbd770a624..2251f34e2c7c7 100644 --- a/integration-tests/kubernetes/standard/src/test/resources/openshift-with-application.properties +++ b/integration-tests/kubernetes/standard/src/test/resources/openshift-with-application.properties @@ -1,9 +1,7 @@ quarkus.http.port=9090 -kubernetes.deployment.target=openshift -openshift.name=test-it -openshift.labels[0].key=foo -openshift.labels[0].value=bar -openshift.env-vars[0].name=MY_ENV_VAR -openshift.env-vars[0].value=SOMEVALUE -openshift.group=grp -s2i.registry=quay.io \ No newline at end of file +quarkus.kubernetes.deployment-target=openshift +quarkus.openshift.name=test-it +quarkus.openshift.labels.foo=bar +quarkus.openshift.env-vars.my-env-var.value=SOMEVALUE +quarkus.openshift.group=grp +quarkus.s2i.registry=quay.io diff --git a/integration-tests/kubernetes/standard/src/test/resources/openshift.properties b/integration-tests/kubernetes/standard/src/test/resources/openshift.properties index 8d388b375415c..f03130f42b7e8 100644 --- a/integration-tests/kubernetes/standard/src/test/resources/openshift.properties +++ b/integration-tests/kubernetes/standard/src/test/resources/openshift.properties @@ -1 +1 @@ -kubernetes.deployment.target=openshift \ No newline at end of file +quarkus.kubernetes.deployment-target=openshift