From 703f84c3b6f24111dc4f3c8893e888740ca362b2 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 14 Mar 2024 16:35:19 -0400 Subject: [PATCH 001/356] Update GitHub actions for 4.2 --- .github/dependabot.yml | 25 ------ .github/workflows/publish-gh-pages.yml | 89 ------------------- .../publish-latest-minor-gh-pages.yml | 64 +++++++++++++ .github/workflows/release.yml | 2 +- 4 files changed, 65 insertions(+), 115 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/publish-gh-pages.yml create mode 100644 .github/workflows/publish-latest-minor-gh-pages.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 662960d8fe6..00000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,25 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "maven" - directory: "/" - schedule: - interval: "daily" - - package-ecosystem: "maven" - target-branch: release/4.1 - directory: "/" - schedule: - interval: "daily" - - package-ecosystem: "maven" - target-branch: release/3.4 - directory: "/" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" diff --git a/.github/workflows/publish-gh-pages.yml b/.github/workflows/publish-gh-pages.yml deleted file mode 100644 index e7fce508653..00000000000 --- a/.github/workflows/publish-gh-pages.yml +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) 2021, 2023, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -# -# Description -# Use Hugo to build static site and publish to gh-pages -# -name: "PublishGitHubPages" - -on: - schedule: - - cron: '15 3 * * *' - -defaults: - run: - shell: bash - -jobs: - publish: - name: Publish - runs-on: ubuntu-latest - - steps: - - name: Checkout branch - uses: actions/checkout@v4 - with: - fetch-depth: 32 - ref: release/4.1 - path: branch-4.1 - - - name: Checkout branch - uses: actions/checkout@v4 - with: - fetch-depth: 32 - ref: release/4.0 - path: branch-4.0 - - - name: Checkout branch - uses: actions/checkout@v4 - with: - fetch-depth: 32 - ref: release/3.4 - path: branch-3.4 - - - name: Checkout gh-pages - uses: actions/checkout@v4 - with: - ref: gh-pages - path: gh-pages - token: ${{ secrets.PUBLISH_SECRET }} - - - name: Build and publish site - run: | - curl -fL -o hugo.tar.gz "https://github.com/gohugoio/hugo/releases/download/v0.108.0/hugo_0.108.0_Linux-64bit.tar.gz" - tar -xf hugo.tar.gz - export PATH="$PWD:$PATH" - mkdir $GITHUB_WORKSPACE/WORK - - cp -R $GITHUB_WORKSPACE/gh-pages/charts $GITHUB_WORKSPACE/WORK - - cd $GITHUB_WORKSPACE/branch-4.1/documentation - echo "Building documentation for 4.1 minor version..." - latest_41_tag=$(git ls-remote https://github.com/oracle/weblogic-kubernetes-operator.git --h --sort origin "refs/tags/v*" | cut -f2 | grep v4.1 | tail -1 | cut -c12-) - echo "Latest tag is $latest_41_tag..." - echo $latest_41_tag >| $GITHUB_WORKSPACE/branch-4.1/documentation/site/layouts/shortcodes/latestVersion.html - hugo -s site -d "$GITHUB_WORKSPACE/WORK" -b https://oracle.github.io/weblogic-kubernetes-operator - echo "Copying static files into place..." - cp -R domains "$GITHUB_WORKSPACE/WORK" - - cd $GITHUB_WORKSPACE/branch-4.0/documentation - echo "Building documentation for 4.0 minor version..." - latest_40_tag=$(git ls-remote https://github.com/oracle/weblogic-kubernetes-operator.git --h --sort origin "refs/tags/v*" | cut -f2 | grep v4.0 | tail -1 | cut -c12-) - echo "Latest tag is $latest_40_tag..." - echo $latest_40_tag >| $GITHUB_WORKSPACE/branch-4.0/documentation/site/layouts/shortcodes/latestVersion.html - hugo -s site -d "$GITHUB_WORKSPACE/WORK/4.0" -b https://oracle.github.io/weblogic-kubernetes-operator/4.0 - - cd $GITHUB_WORKSPACE/branch-3.4/documentation - echo "Building documentation for 3.4 minor version..." - hugo -s site -d "$GITHUB_WORKSPACE/WORK/3.4" -b https://oracle.github.io/weblogic-kubernetes-operator/3.4 - - cd $GITHUB_WORKSPACE/gh-pages - find . -maxdepth 1 -mindepth 1 -not -name '[0-9]*' -not -name '.git*' -exec rm -Rf {} \; - rm -Rf 3.4 4.0 - - cp -R $GITHUB_WORKSPACE/WORK/* . - git config --global user.name "github-actions[bot]" - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add --all - git commit -m "Documentation update from publish GitHub Action" - git push origin gh-pages \ No newline at end of file diff --git a/.github/workflows/publish-latest-minor-gh-pages.yml b/.github/workflows/publish-latest-minor-gh-pages.yml new file mode 100644 index 00000000000..bc940b69291 --- /dev/null +++ b/.github/workflows/publish-latest-minor-gh-pages.yml @@ -0,0 +1,64 @@ +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# +# Description +# Use Hugo to build static site and publish to gh-pages +# +name: "PublishLatestMinorGitHubPages" + +on: + push: + branches: + - release/4.2 + paths: + - 'documentation/**' + +defaults: + run: + shell: bash + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + + steps: + - name: Checkout branch + uses: actions/checkout@v4 + with: + fetch-depth: 32 + path: branch + + - name: Checkout gh-pages + uses: actions/checkout@v4 + with: + ref: gh-pages + path: gh-pages + token: ${{ secrets.PUBLISH_SECRET }} + + - name: Build and publish site + run: | + curl -fL -o hugo.tar.gz "https://github.com/gohugoio/hugo/releases/download/v0.108.0/hugo_0.108.0_Linux-64bit.tar.gz" + tar -xf hugo.tar.gz + export PATH="$PWD:$PATH" + mkdir $GITHUB_WORKSPACE/WORK + + cp -R $GITHUB_WORKSPACE/gh-pages/charts $GITHUB_WORKSPACE/WORK + + cd $GITHUB_WORKSPACE/branch/documentation + echo "Documentation branch is $GITHUB_REF_NAME..." + latest_tag=$(git ls-remote https://github.com/oracle/weblogic-kubernetes-operator.git --h --sort origin "refs/tags/v*" | cut -f2 | grep v4.2 | tail -1 | cut -c12-) + echo "Latest tag is $latest_tag..." + echo $latest_tag >| $GITHUB_WORKSPACE/branch/documentation/site/layouts/shortcodes/latestVersion.html + echo "Building documentation for latest minor version..." + hugo -s site -d "$GITHUB_WORKSPACE/WORK" -b https://oracle.github.io/weblogic-kubernetes-operator + echo "Copying static files into place..." + cp -R domains "$GITHUB_WORKSPACE/WORK" + cd $GITHUB_WORKSPACE/gh-pages + find . -maxdepth 1 -mindepth 1 -not -name '[0-9]*' -not -name '.git*' -exec rm -Rf {} \; + cp -R $GITHUB_WORKSPACE/WORK/* . + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add --all + git commit -m "Documentation update from publish GitHub Action" + git push origin gh-pages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f383845be6..0719010d31f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,7 @@ jobs: gh release create ${{ github.ref_name }} \ --draft \ --generate-notes \ - --target release/4.1 \ + --target release/4.2 \ --title 'Operator ${{ env.VERSION }}' \ --repo https://github.com/oracle/weblogic-kubernetes-operator \ domain-upgrader/target/domain-upgrader.jar From 7fe1e30816d7e2a2a4525fb23ca7b8c48b2d0310 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 06:40:47 -0400 Subject: [PATCH 002/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 26cbf4a7d51..4b7d6175493 100644 --- a/pom.xml +++ b/pom.xml @@ -671,12 +671,12 @@ 3.6.3 3.2.5 3.6.1 - 3.5.1 + 3.5.2 3.1.1 10.13.0 1.0 3.3.2 - 3.1.0 + 3.2.0 2.0.0.0 1.3.3 2.0.1 @@ -696,9 +696,9 @@ 4.2.0 19.0.1 3.0.1u2 - 1.9.22 + 1.9.23 4.12.0 - 3.7.0 + 3.9.0 1.77 5.10.2 5.7.1 From 1e641f51e670e3bfa1b510833489ab877c1edd97 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 07:50:49 -0400 Subject: [PATCH 003/356] Use WME 2.2.0 --- documentation/domains/Domain.json | 4 ++-- documentation/domains/Domain.md | 2 +- kubernetes/crd/domain-crd.yaml | 6 +++--- .../oracle/kubernetes/operator/KubernetesConstants.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index f928676f75a..2814b2d85d8 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -879,8 +879,8 @@ "type": "object", "properties": { "image": { - "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.1.9", - "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.1.9", + "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0", + "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0", "type": "string" }, "imagePullPolicy": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 724d760f6f9..a3f84cac118 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -145,7 +145,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | Name | Type | Description | | --- | --- | --- | | `configuration` | Map | The configuration for the WebLogic Monitoring Exporter. If WebLogic Server instances are already running and have the monitoring exporter sidecar container, then changes to this field will be propagated to the exporter without requiring the restart of the WebLogic Server instances. | -| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.1.9 | +| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 | | `imagePullPolicy` | string | The image pull policy for the WebLogic Monitoring Exporter sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. | | `port` | integer | The port exposed by the WebLogic Monitoring Exporter running in the sidecar container. Defaults to 8080. The port value must not conflict with a port used by any WebLogic Server instance, including the ports of built-in channels or network access points (NAPs). | | `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. | diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index ef7c3cbe927..3de269e2e40 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 00d086baa58ae8ac5b6cc4f5a7a21535833aea22b5d31b5f38b98bcfa2521805 + weblogic.sha256: 8eb63425366f1b880048f26915d7a1943d788a13ed7a483aeb8081848d4ea971 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -47,9 +47,9 @@ spec: appropriate. See https://github.com/oracle/weblogic-monitoring-exporter. properties: image: - default: ghcr.io/oracle/weblogic-monitoring-exporter:2.1.9 + default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 description: The WebLogic Monitoring Exporter sidecar container - image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.1.9 + image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 type: string imagePullPolicy: description: The image pull policy for the WebLogic Monitoring diff --git a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java index 42493449dc6..27778fce34e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java @@ -8,7 +8,7 @@ /** Kubernetes constants. */ public interface KubernetesConstants { String DEFAULT_IMAGE = "container-registry.oracle.com/middleware/weblogic:12.2.1.4"; - String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.1.9"; + String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0"; String DEFAULT_FLUENTD_IMAGE = "fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2"; String EXPORTER_CONTAINER_NAME = "monitoring-exporter"; String LATEST_IMAGE_SUFFIX = ":latest"; From 1155aff236b7dff8c05791250e61ebeb60f1e776 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 08:06:18 -0400 Subject: [PATCH 004/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4b7d6175493..9325e12e10f 100644 --- a/pom.xml +++ b/pom.xml @@ -673,7 +673,7 @@ 3.6.1 3.5.2 3.1.1 - 10.13.0 + 10.14.1 1.0 3.3.2 3.2.0 @@ -710,8 +710,8 @@ 4.0.2 6.0.0 0.16.0 - 2.16.1 - 2.16.1 + 2.17.0 + 2.17.0 2.2 2.10.1 9.0.9 From b03b452a13927f03d2a739a614a6c89e65202c91 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 08:28:47 -0400 Subject: [PATCH 005/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9325e12e10f..b77955196be 100644 --- a/pom.xml +++ b/pom.xml @@ -672,7 +672,7 @@ 3.2.5 3.6.1 3.5.2 - 3.1.1 + 3.2.0 10.14.1 1.0 3.3.2 @@ -714,7 +714,7 @@ 2.17.0 2.2 2.10.1 - 9.0.9 + 9.0.10 2.0.12 1.4.14 ${project.basedir}/src-generated-swagger From cbd37a363f1ac7cf7e2937dceef276d890091d7b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 09:10:56 -0400 Subject: [PATCH 006/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b77955196be..cefa3dba28c 100644 --- a/pom.xml +++ b/pom.xml @@ -716,7 +716,7 @@ 2.10.1 9.0.10 2.0.12 - 1.4.14 + 1.5.3 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 9143ceb306fa84c7211b3ec79751b1db9cb5d19e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 09:22:39 -0400 Subject: [PATCH 007/356] Prepare for WKO 4.2.0 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index d0fb97bf0d4..e6bd9792bde 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 003e3a63192..311401036cc 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index b0697ac8963..9398c877512 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index f7021ef6fbc..22c0aaa1995 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.0-SNAPSHOT + 4.2.0 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 23dea47f03e..792b41f4845 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index a5704ef42e1..7bc561f26f9 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.0-SNAPSHOT + 4.2.0 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 9d365fee964..7e3f1fe2164 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index cefa3dba28c..b8c30804751 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 213a8059f71..d21bddfa3a9 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0-SNAPSHOT + 4.2.0 operator-swagger From d2cb1f33f319a6b69acccd1aa9505bebe02463ce Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 09:52:25 -0400 Subject: [PATCH 008/356] Dependency updates --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index e6bd9792bde..41ea968a07e 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 311401036cc..97296072f26 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 9398c877512..aaf3f48aa1c 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 22c0aaa1995..f7f22d0e3dd 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.0 + 4.2.1-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 792b41f4845..c230262515a 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 7bc561f26f9..492847b32ea 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.0 + 4.2.1-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 7e3f1fe2164..13c3a115d71 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index b8c30804751..fd87d5e055c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index d21bddfa3a9..750dcd2b588 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.0 + 4.2.1-SNAPSHOT operator-swagger From 9109fb8f44df0fe34f1594b32c2012d2f6753ee0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 10:56:37 -0400 Subject: [PATCH 009/356] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 467e8e3b879..591919c722d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Documentation for the operator is [available here](https://oracle.github.io/webl This documentation includes information for users and for developers. It provides samples, reference material, security information and a [Quick Start](https://oracle.github.io/weblogic-kubernetes-operator/quickstart/) guide if you just want to get up and running quickly. -Documentation for prior releases of the operator: [3.4](https://oracle.github.io/weblogic-kubernetes-operator/3.4/) and [4.0](https://oracle.github.io/weblogic-kubernetes-operator/4.0/). +Documentation for prior releases of the operator: [3.4](https://oracle.github.io/weblogic-kubernetes-operator/3.4/), [4.0](https://oracle.github.io/weblogic-kubernetes-operator/4.0/), and [4.1](https://oracle.github.io/weblogic-kubernetes-operator/4.1/). ## Backward compatibility guidelines @@ -60,7 +60,7 @@ Please consult the [security guide](./SECURITY.md) for our responsible security ## License -Copyright (c) 2017, 2022 Oracle and/or its affiliates. +Copyright (c) 2017, 2024, Oracle and/or its affiliates. Released under the Universal Permissive License v1.0 as shown at . From 7026b66eb67d633a84b593a41f93b611b4342b2b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 15 Mar 2024 10:59:49 -0400 Subject: [PATCH 010/356] Update documentation about prior releases --- documentation/site/content/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/_index.md b/documentation/site/content/_index.md index 151562d7432..7dc353cb79f 100644 --- a/documentation/site/content/_index.md +++ b/documentation/site/content/_index.md @@ -43,7 +43,7 @@ See the [Release Notes](https://github.com/oracle/weblogic-kubernetes-operator/r #### Operator earlier versions -Documentation for supported prior releases of the operator: [3.4](https://oracle.github.io/weblogic-kubernetes-operator/3.4/) and [4.0](https://oracle.github.io/weblogic-kubernetes-operator/4.0/). +Documentation for supported prior releases of the operator: [3.4](https://oracle.github.io/weblogic-kubernetes-operator/3.4/), [4.0](https://oracle.github.io/weblogic-kubernetes-operator/4.0/), and [4.1](https://oracle.github.io/weblogic-kubernetes-operator/4.1/). #### Backward compatibility guidelines From d75a57a1d5137eab37c58faf2e51c27a59c7de73 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sun, 17 Mar 2024 14:00:51 +0000 Subject: [PATCH 011/356] Merge branch 'temp-fix' into 'main' Separate rolling-restart from ItFmwDomaininPV gating test method See merge request weblogic-cloud/weblogic-kubernetes-operator!4622 (cherry picked from commit c8d2841413a0aa63813c4fae9e94ee61a4dc3687) 0db85a12 separate rolling-restart from gating test method --- .../weblogic/kubernetes/ItFmwDomainOnPV.java | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index 27ec73adcfa..280f2ca1493 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -168,7 +168,6 @@ public static void initAll(@Namespaces(3) List namespaces) { * Create a basic FMW domain on PV. * Operator will create PV/PVC/RCU/Domain. * Verify Pod is ready and service exists for both admin server and managed servers. - * Update the base image in the domain spec, verify the domain is rolling-restarted. */ @Test @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") @@ -254,42 +253,6 @@ void testOperatorCreatesPvPvcRcuDomain() { // verify that all servers are ready verifyDomainReady(domainNamespace, domainUid, replicaCount, "nosuffix"); - // get the map with server pods and their original creation timestamps - String adminServerPodName = domainUid + "-admin-server"; - String managedServerPodNamePrefix = domainUid + "-managed-server"; - Map podsWithTimeStamps = getPodsWithTimeStamps(domainNamespace, - adminServerPodName, managedServerPodNamePrefix, replicaCount); - - // update the domain with new base image - int index = FMWINFRA_IMAGE_TO_USE_IN_SPEC.lastIndexOf(":"); - String newImage; - if (OCNE) { - newImage = BASE_IMAGES_PREFIX + "fmw-infrastructure1:newtag"; - } else { - newImage = FMWINFRA_IMAGE_TO_USE_IN_SPEC.substring(0, index) + ":newtag"; - } - testUntil( - tagImageAndPushIfNeeded(FMWINFRA_IMAGE_TO_USE_IN_SPEC, newImage), - logger, - "tagImageAndPushIfNeeded for image {0} to be successful", - newImage); - - logger.info("patch the domain resource with new image {0}", newImage); - String patchStr - = "[" - + "{\"op\": \"replace\", \"path\": \"/spec/image\", " - + "\"value\": \"" + newImage + "\"}" - + "]"; - logger.info("Updating domain configuration using patch string: {0}\n", patchStr); - V1Patch patch = new V1Patch(patchStr); - assertTrue(patchDomainCustomResource(domainUid, domainNamespace, patch, V1Patch.PATCH_FORMAT_JSON_PATCH), - "Failed to patch domain"); - - // verify the server pods are rolling restarted and back to ready state - logger.info("Verifying rolling restart occurred for domain {0} in namespace {1}", - domainUid, domainNamespace); - assertTrue(verifyRollingRestartOccurred(podsWithTimeStamps, 1, domainNamespace), - String.format("Rolling restart failed for domain %s in namespace %s", domainUid, domainNamespace)); } finally { // delete the domain deleteDomainResource(domainNamespace, domainUid); @@ -304,6 +267,7 @@ void testOperatorCreatesPvPvcRcuDomain() { * Create a basic FMW domain on PV. * User creates PV/PVC, operator creates RCU and domain. * Verify Pod is ready and service exists for both admin server and managed servers. + * Update the base image in the domain spec, verify the domain is rolling-restarted. */ @Test @DisplayName("Create a FMW domain on PV. User creates PV/PVC and operator creates RCU and domain") @@ -384,6 +348,8 @@ void testUserCreatesPvPvcOperatorCreatesRcuDomain() { // verify that all servers are ready verifyDomainReady(domainNamespace, domainUid, replicaCount, "nosuffix"); + + verifyRollingRestartWithImageChg(domainUid, domainNamespace); } finally { // delete the domain deleteDomainResource(domainNamespace, domainUid); @@ -820,4 +786,43 @@ private Callable tagImageAndPushIfNeeded(String originalImage, String t return result; }); } + + private void verifyRollingRestartWithImageChg(String domainUid, String domainNamespace) { + // get the map with server pods and their original creation timestamps + String adminServerPodName = domainUid + "-admin-server"; + String managedServerPodNamePrefix = domainUid + "-managed-server"; + Map podsWithTimeStamps = getPodsWithTimeStamps(domainNamespace, + adminServerPodName, managedServerPodNamePrefix, replicaCount); + + // update the domain with new base image + int index = FMWINFRA_IMAGE_TO_USE_IN_SPEC.lastIndexOf(":"); + String newImage; + if (OCNE) { + newImage = BASE_IMAGES_PREFIX + "fmw-infrastructure1:newtag"; + } else { + newImage = FMWINFRA_IMAGE_TO_USE_IN_SPEC.substring(0, index) + ":newtag"; + } + testUntil( + tagImageAndPushIfNeeded(FMWINFRA_IMAGE_TO_USE_IN_SPEC, newImage), + logger, + "tagImageAndPushIfNeeded for image {0} to be successful", + newImage); + + logger.info("patch the domain resource with new image {0}", newImage); + String patchStr + = "[" + + "{\"op\": \"replace\", \"path\": \"/spec/image\", " + + "\"value\": \"" + newImage + "\"}" + + "]"; + logger.info("Updating domain configuration using patch string: {0}\n", patchStr); + V1Patch patch = new V1Patch(patchStr); + assertTrue(patchDomainCustomResource(domainUid, domainNamespace, patch, V1Patch.PATCH_FORMAT_JSON_PATCH), + "Failed to patch domain"); + + // verify the server pods are rolling restarted and back to ready state + logger.info("Verifying rolling restart occurred for domain {0} in namespace {1}", + domainUid, domainNamespace); + assertTrue(verifyRollingRestartOccurred(podsWithTimeStamps, 1, domainNamespace), + String.format("Rolling restart failed for domain %s in namespace %s", domainUid, domainNamespace)); + } } From f3385d6fc5d22f6de02a01af8e8c1835dbd675d4 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 18 Mar 2024 12:35:26 -0400 Subject: [PATCH 012/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index fd87d5e055c..4f3257b175b 100644 --- a/pom.xml +++ b/pom.xml @@ -673,17 +673,17 @@ 3.6.1 3.5.2 3.2.0 - 10.14.1 + 10.14.2 1.0 3.3.2 - 3.2.0 + 3.2.1 2.0.0.0 1.3.3 2.0.1 2.0.1 1.0.39 1.6.0 - 1.3.3 + 1.4.0 1.4.0 1.16.1 1.7.3 @@ -693,7 +693,7 @@ 1.0.0 3.25.3 2.15.1 - 4.2.0 + 4.2.1 19.0.1 3.0.1u2 1.9.23 From d58841457d10690716e06275bb6947fcdb624b48 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 20 Mar 2024 09:10:37 -0400 Subject: [PATCH 013/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4f3257b175b..71afb308148 100644 --- a/pom.xml +++ b/pom.xml @@ -659,7 +659,7 @@ [3.8.1,) 3.4.1 3.3.2 - 3.12.1 + 3.13.0 3.1.1 3.1.1 3.3.0 From de33fc9b81d22e9617a8ff3a616397e871f695bc Mon Sep 17 00:00:00 2001 From: "ANTARYAMI.PANIGRAHI" Date: Wed, 20 Mar 2024 21:18:30 +0000 Subject: [PATCH 014/356] Adding Daily cron Job for Release/4.2 --- Jenkinsfile.kindnightly | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Jenkinsfile.kindnightly b/Jenkinsfile.kindnightly index 57a0829a78a..63588ddcdae 100644 --- a/Jenkinsfile.kindnightly +++ b/Jenkinsfile.kindnightly @@ -20,11 +20,7 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential - H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.0;KIND_VERSION=0.19.0 - H 4 * * * % MAVEN_PROFILE_NAME=ipv6;IP_FAMILY=ipv6''' - +CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=wls-srg''' pipeline { agent { label 'large' } @@ -33,7 +29,7 @@ pipeline { } triggers { // timer trigger for "nightly build" - parameterizedCron(env.JOB_NAME == 'wko-kind-main-nightly' ? + parameterizedCron(env.JOB_NAME == 'wko-kind-release42-nightly' ? CRON_SETTINGS : '') } @@ -67,7 +63,7 @@ pipeline { parameters { string(name: 'BRANCH', description: 'The branch to run the tests on', - defaultValue: 'main' + defaultValue: 'release/4.2' ) choice(name: 'IP_FAMILY', description: 'Ip Family.', @@ -80,12 +76,12 @@ pipeline { choice(name: 'MAVEN_PROFILE_NAME', description: 'Profile to use in mvn command to run the tests. Possible values are kind-parallel (the default), kind-sequential, kind-upgrade,toolkits-srg, wls-srg and integration-tests. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', choices: [ + 'wls-srg', 'kind-parallel', 'kind-sequential', 'kind-upgrade', 'integration-tests', 'toolkits-srg', - 'wls-srg', 'ipv6' ] ) From 8dbeb8c40aaaed12a55db0b2d27af3cccca00844 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 27 Mar 2024 19:49:10 +0000 Subject: [PATCH 015/356] Merge branch 'owls-116638-fix-duplicate-volumes-added' into 'main' Fix duplicate volumes are added if using server pod spec in initcontainer,... See merge request weblogic-cloud/weblogic-kubernetes-operator!4633 (cherry picked from commit d57345fd65c18eff983647c5b77256eb20b8d958) e470a9ec Fix duplicate volumes are added if using server pod spec in initcontainer,... dd603892 remove debug logging 21b6bc71 remove whenAdminServerHasNormalAdditionalVolume_introspectorPodStartupWithoutThem test. eca57ff3 style fix --- .../kubernetes/operator/helpers/JobStepContext.java | 11 ++++++++++- .../kubernetes/operator/helpers/JobHelperTest.java | 11 ----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 04f6f26ea39..8c6361e442c 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -482,7 +482,7 @@ private void addInitDomainOnPVInitContainer(List initContainers) { initContainers.add(new V1Container() .name(INIT_DOMAIN_ON_PV_CONTAINER) .image(getDomain().getSpec().getImage()) - .volumeMounts(getDomain().getSpec().getAdditionalVolumeMounts()) + .volumeMounts(getDomain().getAdminServerSpec().getAdditionalVolumeMounts()) .addVolumeMountsItem(new V1VolumeMount().name(SCRIPTS_VOLUME).mountPath(SCRIPTS_MOUNTS_PATH)) .addVolumeMountsItem(new V1VolumeMount().name(AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME) .mountPath(AUXILIARY_IMAGE_TARGET_PATH)) @@ -600,6 +600,15 @@ protected V1PodSpec createPodSpec() { podSpec.addVolumesItem(additionalVolume); } + for (V1Volume additionalVolume : info.getDomain().getAdminServerSpec().getAdditionalVolumes()) { + if (podSpec.getVolumes() != null) { + List volumes = podSpec.getVolumes(); + if (!volumes.contains(additionalVolume) && !additionalVolume.getName().startsWith(COMPATIBILITY_MODE)) { + volumes.add(additionalVolume); + } + } + } + getConfigOverrideSecrets().forEach(secretName -> addConfigOverrideSecretVolume(podSpec, secretName)); Optional.ofNullable(getConfigOverrides()).ifPresent(overrides -> addConfigOverrideVolume(podSpec, overrides)); diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java index acd387006fe..a53ef766d29 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java @@ -1707,17 +1707,6 @@ void whenAdminServerHasNormalInitContainers_introspectorPodStartupWithoutThem() assertThat(getNumPodSpecInitContainers(jobSpec), equalTo(0)); } - @Test - void whenAdminServerHasNormalAdditionalVolume_introspectorPodStartupWithoutThem() { - configureDomain() - .configureAdminServer() - .withAdditionalVolume("Test", "/test"); - - V1JobSpec jobSpec = createJobSpec(); - - assertThat(getNumPodSpecVolumes(jobSpec), equalTo(3)); - } - private int getNumPodSpecVolumes(V1JobSpec jobSpec) { return getPodSpecListSize(jobSpec, V1PodSpec::getVolumes); } From 6cc3906342c891f1b839cfbf194fcc6430d0d467 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 27 Mar 2024 18:02:31 -0400 Subject: [PATCH 016/356] Use provenance flag --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0719010d31f..ea59b19d18f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,6 +74,7 @@ jobs: context: . platforms: linux/amd64,linux/arm64 push: true + provenance: false tags: ${{ env.IMAGE_NAME }} - name: Checkout gh-pages From 66dceda8c09a6212340c9d658fe337dc779fb9a2 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Thu, 28 Mar 2024 21:54:15 +0000 Subject: [PATCH 017/356] backport MR 4630 to release/4.2 --- ...DiagnosticsCompleteAvailableCondition.java | 22 +- .../kubernetes/ItKubernetesDomainEvents.java | 18 +- .../actions/impl/primitive/Kubernetes.java | 7 +- .../assertions/impl/Kubernetes.java | 13 +- .../kubernetes/utils/CleanupUtil.java | 30 ++- .../weblogic/kubernetes/utils/K8sEvents.java | 216 ++++++++++-------- 6 files changed, 170 insertions(+), 136 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java index e47d3703c18..b38ede05812 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java @@ -67,8 +67,6 @@ class ItDiagnosticsCompleteAvailableCondition { private static final String cluster1Name = "cluster-1"; - private static final String clusterResName = cluster1Name; - private static LoggingFacade logger = null; private static String domainNamespace1 = null; private static int replicaCount = 2; @@ -132,7 +130,7 @@ void testCompleteAvailableConditionWithIfNeeded() { verifyDomainStatusConditionTypeDoesNotExist(domainUid, domainNamespace1, DOMAIN_STATUS_CONDITION_FAILED_TYPE); } finally { deleteDomainResource(domainNamespace1, domainUid); - deleteClusterCustomResource(clusterResName, domainNamespace1); + deleteClusterCustomResource(domainUid + "-" + cluster1Name, domainNamespace1); } } @@ -183,7 +181,7 @@ void testCompleteAvailableConditionWithAdminOnly() { verifyDomainStatusConditionTypeDoesNotExist(domainUid, domainNamespace1, DOMAIN_STATUS_CONDITION_FAILED_TYPE); } finally { deleteDomainResource(domainNamespace1, domainUid); - deleteClusterCustomResource(clusterResName, domainNamespace1); + deleteClusterCustomResource(domainUid + "-" + cluster1Name, domainNamespace1); } } @@ -235,7 +233,7 @@ void testCompleteAvailableConditionWithNever() { } finally { deleteDomainResource(domainNamespace1, domainUid); - deleteClusterCustomResource(clusterResName, domainNamespace1); + deleteClusterCustomResource(domainUid + "-" + cluster1Name, domainNamespace1); } } @@ -255,7 +253,9 @@ void testCompleteAvailableConditionWithReplicaZero() { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; String patchStr; + try { logger.info("patch the cluster resource with new cluster replica 0"); patchStr @@ -310,6 +310,7 @@ void testCompleteAvailableConditionWithClusterNever() { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; String patchStr; try { logger.info("patch the cluster resource with cluster serverStartPolicy to Never"); @@ -355,6 +356,7 @@ void testCompleteAvailableConditionWithClusterNever() { @DisplayName("Test domain status condition with cluster replica set to larger than max size of cluster") void testCompleteAvailableConditionWithReplicaExceedMaxSizeWithoutChangingIntrospectVersion() { String domainUid = "diagnosticsdomain6"; + String clusterResName = domainUid + "-" + cluster1Name; createDomainAndVerify(domainUid); try { @@ -391,6 +393,7 @@ void testCompleteAvailableConditionWithReplicaExceedMaxSizeAndIntrospectVersionC String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; String patchStr; try { int newReplicaCount = maxClusterSize + 1; @@ -449,6 +452,7 @@ void testCompleteAvailableConditionWithReplicaLessThanMaxSize() { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; String patchStr; try { logger.info("patch the domain resource with replica less than max size of cluster"); @@ -510,6 +514,7 @@ void testCompleteAvailableConditionWithScaleUpDownCluster() { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; try { // scale down the cluster @@ -592,6 +597,7 @@ void testCompleteAvailableConditionWithNewRestartVersion() { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; try { // get the pod creation time stamps @@ -658,6 +664,7 @@ void testCompleteAvailableConditionWithNewImage() { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; String managedServerPodNamePrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + String clusterResName = domainUid + "-" + cluster1Name; try { // get the pod creation time stamps @@ -733,6 +740,9 @@ private void createDomainAndVerify(String domainUid) { adminServerPodName, managedServerPodNamePrefix, replicaCount, - List.of(cluster1Name)); + List.of(cluster1Name), + false, + null, + true); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index cc8452d5f6a..120b2b59c1c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -27,12 +27,10 @@ import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; -import oracle.weblogic.kubernetes.actions.impl.OperatorParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.DomainUtils; -import oracle.weblogic.kubernetes.utils.K8sEvents; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; @@ -55,7 +53,6 @@ import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; import static oracle.weblogic.kubernetes.actions.TestActions.getDomainCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.getNextIntrospectVersion; -import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.getServicePort; import static oracle.weblogic.kubernetes.actions.TestActions.now; import static oracle.weblogic.kubernetes.actions.TestActions.scaleCluster; @@ -101,7 +98,7 @@ import static oracle.weblogic.kubernetes.utils.K8sEvents.checkDomainEventWithCount; import static oracle.weblogic.kubernetes.utils.K8sEvents.checkDomainFailedEventWithReason; import static oracle.weblogic.kubernetes.utils.K8sEvents.getDomainEventCount; -import static oracle.weblogic.kubernetes.utils.K8sEvents.getOpGeneratedEventCount; +import static oracle.weblogic.kubernetes.utils.K8sEvents.getOpGeneratedEventCountForResource; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResource; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; @@ -146,8 +143,6 @@ class ItKubernetesDomainEvents { private static String domainNamespace4 = null; private static String domainNamespace5 = null; private static String opServiceAccount = null; - private static int externalRestHttpsPort = 0; - private static OperatorParams opParams = null; static final String cluster1Name = "mycluster"; static final String cluster2Name = "cl2"; @@ -211,10 +206,9 @@ public static void initAll(@Namespaces(6) List namespaces) { opServiceAccount = opNamespace + "-sa"; // install and verify operator with REST API - opParams = installAndVerifyOperator(opNamespace, opServiceAccount, + installAndVerifyOperator(opNamespace, opServiceAccount, true, 0, domainNamespace1, domainNamespace2, domainNamespace3, domainNamespace4, domainNamespace5); - externalRestHttpsPort = getServiceNodePort(opNamespace, "external-weblogic-operator-svc"); createDomain(domainNamespace3, domainUid, pvName3, pvcName3); } @@ -387,9 +381,7 @@ void testK8SEventsMultiClusterEvents() { logger.info("verify the Cluster_Available event is generated"); checkEvent(opNamespace, domainNamespace3, domainUid, CLUSTER_CHANGED, "Normal", timestamp); - assertEquals(1, getOpGeneratedEventCount(domainNamespace3, domainUid, - CLUSTER_CHANGED, timestamp2)); - assertEquals(1, K8sEvents.getOpGeneratedEventCountForResource(domainNamespace3, domainUid, cluster2Name, + assertEquals(1, getOpGeneratedEventCountForResource(domainNamespace3, domainUid, cluster2Name, CLUSTER_CHANGED, timestamp)); checkEvent(opNamespace, domainNamespace3, domainUid, CLUSTER_AVAILABLE, "Normal", timestamp); @@ -400,10 +392,10 @@ void testK8SEventsMultiClusterEvents() { checkEvent(opNamespace, domainNamespace3, domainUid, CLUSTER_COMPLETED, "Normal", timestamp2); logger.info("verify the only 1 Completed event for domain is generated"); - assertEquals(1, getOpGeneratedEventCount(domainNamespace3, domainUid, + assertEquals(1, getOpGeneratedEventCountForResource(domainNamespace3, domainUid, domainUid, DOMAIN_COMPLETED, timestamp)); logger.info("verify the only 1 ClusterCompleted event for domain is generated"); - assertEquals(1, getOpGeneratedEventCount(domainNamespace3, domainUid, + assertEquals(1, getOpGeneratedEventCountForResource(domainNamespace3, domainUid, cluster2Name, CLUSTER_COMPLETED, timestamp2)); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/Kubernetes.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/Kubernetes.java index cbfb8340ada..74d9bbada36 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/Kubernetes.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/Kubernetes.java @@ -2801,9 +2801,10 @@ public static V1JobList listJobs(String namespace) throws ApiException { */ public static V1Job getJob(String jobName, String namespace) throws ApiException { V1JobList listJobs = listJobs(namespace); - for (V1Job job : listJobs.getItems()) { - if (job != null && job.getMetadata() != null) { - if (job.getMetadata().getName().equals(jobName)) { + if (listJobs != null) { + for (V1Job job : listJobs.getItems()) { + if (job.getMetadata() != null && job.getMetadata().getName() != null + && job.getMetadata().getName().equals(jobName)) { return job; } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java index 8ba1a5e28be..8c6212999ea 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.assertions.impl; @@ -783,10 +783,13 @@ public static V1JobList listJobs(String namespace, String labelSelectors) { */ public static V1Job getJob(String namespace, String labelSelectors, String jobName) throws ApiException { - List jobs = listJobs(namespace, labelSelectors).getItems(); - for (V1Job job : jobs) { - if (job != null && job.getMetadata().getName().equals(jobName)) { - return job; + V1JobList jobList = listJobs(namespace, labelSelectors); + if (jobList != null) { + for (V1Job job : jobList.getItems()) { + if (job.getMetadata() != null && job.getMetadata().getName() != null + && job.getMetadata().getName().equals(jobName)) { + return job; + } } } return null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java index 541610134a9..dfad0b89bbb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -13,6 +13,7 @@ import io.kubernetes.client.openapi.models.V1Deployment; import io.kubernetes.client.openapi.models.V1Ingress; import io.kubernetes.client.openapi.models.V1Job; +import io.kubernetes.client.openapi.models.V1JobList; import io.kubernetes.client.openapi.models.V1PersistentVolume; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim; import io.kubernetes.client.openapi.models.V1ReplicaSet; @@ -29,6 +30,7 @@ import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listJobs; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -210,13 +212,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any jobs exist try { - if (!Kubernetes.listJobs(namespace).getItems().isEmpty()) { - logger.info("Jobs still exists!!!"); - List items = Kubernetes.listJobs(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1JobList jobList = listJobs(namespace); + if (jobList != null) { + List items = jobList.getItems(); + if (items != null && !items.isEmpty()) { + logger.info("Jobs still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -456,8 +463,13 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete jobs try { - for (var item : Kubernetes.listJobs(namespace).getItems()) { - Kubernetes.deleteJob(namespace, item.getMetadata().getName()); + V1JobList jobList = listJobs(namespace); + if (jobList != null) { + for (var item : jobList.getItems()) { + if (item.getMetadata() != null) { + Kubernetes.deleteJob(namespace, item.getMetadata().getName()); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java index 2a5bca86ebf..fbfbad24d3a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -15,20 +15,18 @@ import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.CoreV1Event; import io.kubernetes.client.util.Yaml; -import oracle.weblogic.kubernetes.TestConstants; import oracle.weblogic.kubernetes.actions.TestActions; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.logging.LoggingFacade; import org.awaitility.core.ConditionFactory; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; import static oracle.weblogic.kubernetes.actions.TestActions.getOperatorPodName; import static oracle.weblogic.kubernetes.actions.TestActions.getPodLog; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Helper class for Kubernetes Events checking. @@ -95,9 +93,7 @@ public static void checkEvent( public static Callable checkDomainFailedEventWithReason( String opNamespace, String domainNamespace, String domainUid, String failureReason, String type, OffsetDateTime timestamp) { - return () -> { - return domainFailedEventExists(opNamespace, domainNamespace, domainUid, failureReason, type, timestamp); - }; + return () -> domainFailedEventExists(opNamespace, domainNamespace, domainUid, failureReason, type, timestamp); } /** @@ -113,9 +109,7 @@ public static Callable checkDomainFailedEventWithReason( public static Callable checkDomainEvent( String opNamespace, String domainNamespace, String domainUid, String reason, String type, OffsetDateTime timestamp) { - return () -> { - return domainEventExists(opNamespace, domainNamespace, domainUid, reason, type, timestamp); - }; + return () -> domainEventExists(opNamespace, domainNamespace, domainUid, reason, type, timestamp); } /** @@ -190,14 +184,18 @@ public static boolean domainFailedEventExists( try { List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - if (DOMAIN_FAILED.equals(event.getReason()) && (isEqualOrAfter(timestamp, event)) - && event.getMessage().contains(failureReason)) { + if (DOMAIN_FAILED.equals(event.getReason()) && isEqualOrAfter(timestamp, event) + && event.getMessage() != null && event.getMessage().contains(failureReason)) { logger.info(Yaml.dump(event)); - verifyOperatorDetails(event, opNamespace, domainUid); + if (!verifyOperatorDetails(event, opNamespace, domainUid)) { + logger.info("verifyOperatorDetails failed"); + return false; + } //verify type logger.info("Verifying domain event type {0}", type); - assertEquals(event.getType(), type); - return true; + if (event.getType() != null && event.getType().equals(type)) { + return true; + } } } } catch (ApiException ex) { @@ -225,12 +223,16 @@ public static boolean domainEventExists( for (CoreV1Event event : events) { if (isDomainEvent(domainUid, event) && reason.equals(event.getReason()) && isEqualOrAfter(timestamp, event)) { logger.info(Yaml.dump(event)); - verifyOperatorDetails(event, opNamespace, domainUid); + if (!verifyOperatorDetails(event, opNamespace, domainUid)) { + logger.info("verifyOperatorDetails failed"); + return false; + } //verify type logger.info("Verifying domain event type {0} with reason {1} for domain {2} in namespace {3}", type, reason, domainUid, domainNamespace); - assertEquals(event.getType(), type); - return true; + if (event.getType() != null && event.getType().equals(type)) { + return true; + } } } } catch (ApiException ex) { @@ -263,9 +265,9 @@ public static CoreV1Event getOpGeneratedEvent( try { List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - if (event.getReason().equals(reason) && (isEqualOrAfter(timestamp, event))) { + if (event.getReason() != null && event.getReason().equals(reason) && (isEqualOrAfter(timestamp, event))) { logger.info(Yaml.dump(event)); - if (event.getType().equals(type)) { + if (event.getType() != null && event.getType().equals(type)) { return event; } } @@ -295,14 +297,21 @@ public static Callable checkDomainEventWithCount( try { List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - if (((domainUid != null && event.getMetadata().getLabels().containsValue(domainUid)) - || domainUid == null) - && event.getReason().equals(reason) && (isEqualOrAfter(timestamp, event))) { + if ((domainUid == null + || (event.getMetadata() != null && event.getMetadata().getLabels() != null + && event.getMetadata().getLabels().containsValue(domainUid))) + && event.getReason() != null && event.getReason().equals(reason) + && (isEqualOrAfter(timestamp, event))) { logger.info(Yaml.dump(event)); - verifyOperatorDetails(event, opNamespace, domainUid); + if (!verifyOperatorDetails(event, opNamespace, domainUid)) { + logger.info("verifyOperatorDetails failed"); + return false; + } //verify type logger.info("Verifying domain event type {0}", type); - assertEquals(type, event.getType()); + if (event.getType() != null && !event.getType().equals(type)) { + return false; + } int countAfter = getDomainEventCount(domainNamespace, domainUid, reason, "Normal"); return (countAfter >= countBefore + 1); } @@ -328,11 +337,14 @@ public static int getDomainEventCount( try { List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - Map labels = event.getMetadata().getLabels(); - if (event.getReason().equals(reason) - && event.getType().equals(type) - && labels.get("weblogic.domainUID").equals(domainUid)) { - return event.getCount(); + if (event.getMetadata() != null) { + Map labels = event.getMetadata().getLabels(); + if (event.getReason() != null && event.getReason().equals(reason) + && event.getType() != null && event.getType().equals(type) + && labels != null && labels.get("weblogic.domainUID") != null + && labels.get("weblogic.domainUID").equals(domainUid)) { + return event.getCount(); + } } } } catch (ApiException ex) { @@ -341,36 +353,6 @@ public static int getDomainEventCount( return 0; } - /** - * Get the event count between a specific timestamp. - * - * @param domainNamespace namespace in which the domain exists - * @param domainUid UID of the domain - * @param reason event to check for Created, Changed, deleted, processing etc - * @param timestamp the timestamp after which to see events - * @return count number of events count - */ - public static int getOpGeneratedEventCount( - String domainNamespace, String domainUid, String reason, OffsetDateTime timestamp) { - int count = 0; - try { - List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); - for (CoreV1Event event : events) { - Map labels = event.getMetadata().getLabels(); - if (event.getReason().equals(reason) - && labels.get("weblogic.domainUID").equals(domainUid) - && (isEqualOrAfter(timestamp, event))) { - logger.info(Yaml.dump(event)); - count++; - } - } - } catch (ApiException ex) { - Logger.getLogger(K8sEvents.class.getName()).log(Level.SEVERE, null, ex); - return -1; - } - return count; - } - /** * Get the event count between a specific timestamp for the given resource (domain or cluster). * @@ -387,13 +369,17 @@ public static int getOpGeneratedEventCountForResource( try { List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - Map labels = event.getMetadata().getLabels(); - if (event.getReason().equals(reason) - && labels.get("weblogic.domainUID").equals(domainUid) - && event.getInvolvedObject().getName().equals(resourceName) - && (isEqualOrAfter(timestamp, event))) { - logger.info(Yaml.dump(event)); - count++; + if (event.getMetadata() != null) { + Map labels = event.getMetadata().getLabels(); + if (event.getReason() != null && event.getReason().equals(reason) + && labels != null && labels.get("weblogic.domainUID") != null + && labels.get("weblogic.domainUID").equals(domainUid) + && event.getInvolvedObject() != null && event.getInvolvedObject().getName() != null + && event.getInvolvedObject().getName().equals(resourceName) + && (isEqualOrAfter(timestamp, event))) { + logger.info(Yaml.dump(event)); + count++; + } } } } catch (ApiException ex) { @@ -418,8 +404,9 @@ public static Callable checkPodEventLoggedOnce( reason, serverName, domainNamespace); try { return isEventLoggedOnce(serverName, Kubernetes.listNamespacedEvents(domainNamespace).stream() - .filter(e -> e.getInvolvedObject().getName().equals(serverName)) - .filter(e -> e.getReason().contains(reason)) + .filter(e -> e.getInvolvedObject() != null && e.getInvolvedObject().getName() != null + && e.getInvolvedObject().getName().equals(serverName)) + .filter(e -> e.getReason() != null && e.getReason().contains(reason)) .filter(e -> isEqualOrAfter(timestamp, e)).collect(Collectors.toList()).size()); } catch (ApiException ex) { Logger.getLogger(K8sEvents.class.getName()).log(Level.SEVERE, null, ex); @@ -492,15 +479,17 @@ public static List getEvents(String domainNamespace, try { List allEvents = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : allEvents) { - Map labels = event.getMetadata().getLabels(); - if (event.getReason().equals(reason) - && (isEqualOrAfter(timestamp, event)) - && event.getType().equals(type) - && (labels != null) - && (labels.get("weblogic.domainUID") != null) - && labels.get("weblogic.domainUID").equals(domainUid)) { - - events.add(event); + if (event.getMetadata() != null) { + Map labels = event.getMetadata().getLabels(); + if (event.getReason() != null && event.getReason().equals(reason) + && (isEqualOrAfter(timestamp, event)) + && event.getType() != null && event.getType().equals(type) + && labels != null + && labels.get("weblogic.domainUID") != null + && labels.get("weblogic.domainUID").equals(domainUid)) { + + events.add(event); + } } } } catch (ApiException ex) { @@ -534,8 +523,8 @@ public static List getEvents(String namespace, } private static boolean isEqualOrAfter(OffsetDateTime timestamp, CoreV1Event event) { - return event.getLastTimestamp().isEqual(timestamp) - || event.getLastTimestamp().isAfter(timestamp); + return event.getLastTimestamp() != null + && (event.getLastTimestamp().isEqual(timestamp) || event.getLastTimestamp().isAfter(timestamp)); } private static Boolean isEventLoggedOnce(String serverName, int count) { @@ -549,30 +538,57 @@ private static Boolean logErrorAndFail(String serverName, int count) { } // Verify the operator instance details are correct - private static void verifyOperatorDetails( + private static boolean verifyOperatorDetails( CoreV1Event event, String opNamespace, String domainUid) throws ApiException { logger.info("Verifying operator details"); String operatorPodName = TestActions.getOperatorPodName(OPERATOR_RELEASE_NAME, opNamespace); - //verify DOMAIN_API_VERSION - if (domainUid != null && event.getInvolvedObject().getKind().equals("Domain")) { - assertEquals(TestConstants.DOMAIN_API_VERSION, event.getInvolvedObject().getApiVersion(), - "Expected " + TestConstants.DOMAIN_API_VERSION + " ,Got " + event.getInvolvedObject().getApiVersion()); - } - //verify reporting component to be operator release - assertEquals("weblogic.operator", event.getReportingComponent(), - "Didn't get reporting component as " + "weblogic.operator"); - //verify reporting instance to be operator instance - assertEquals(operatorPodName, event.getReportingInstance(), - "Didn't get reporting instance as " + operatorPodName); - //verify the event was created by operator - Map labels = event.getMetadata().getLabels(); - assertTrue(labels.containsKey("weblogic.createdByOperator") - && labels.get("weblogic.createdByOperator").equals("true")); - //verify the domainUID matches - if (domainUid != null) { - assertTrue(labels.containsKey("weblogic.domainUID") - && labels.get("weblogic.domainUID").equals(domainUid)); + + if (event != null) { + //verify DOMAIN_API_VERSION + if (domainUid != null + && event.getInvolvedObject() != null + && event.getInvolvedObject().getKind() != null + && event.getInvolvedObject().getKind().equals("Domain")) { + if (event.getInvolvedObject().getApiVersion() != null + && !event.getInvolvedObject().getApiVersion().equals(DOMAIN_API_VERSION)) { + logger.info("Expected " + DOMAIN_API_VERSION + " , Got " + event.getInvolvedObject().getApiVersion()); + return false; + } + } + + //verify reporting component to be operator release + if (event.getReportingComponent() != null + && !event.getReportingComponent().equals("weblogic.operator")) { + logger.info("Expected reporting component as weblogic.operator, Got: " + event.getReportingComponent()); + return false; + } + + //verify reporting instance to be operator instance + if (event.getReportingInstance() != null + && !event.getReportingInstance().equals(operatorPodName)) { + logger.info("Expect reporting instance as " + operatorPodName + ", Got" + event.getReportingInstance()); + return false; + } + + //verify the event was created by operator + if (event.getMetadata() != null) { + Map labels = event.getMetadata().getLabels(); + if (labels != null + && (!labels.containsKey("weblogic.createdByOperator") + || !labels.get("weblogic.createdByOperator").equals("true"))) { + logger.info("labels do not contain key weblogic.createdByOperator or weblogic.createdByOperator is not true"); + return false; + } + + //verify the domainUID matches + if (domainUid != null && labels != null + && (!labels.containsKey("weblogic.domainUID") || !labels.get("weblogic.domainUID").equals(domainUid))) { + logger.info("labels do not contain key weblogic.domainUID or weblogic.domainUID is not " + domainUid); + return false; + } + } } + return true; } public static final String DOMAIN_AVAILABLE = "Available"; From 8613f9d160dcc1be60628e684be346b8739a4ade Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sun, 31 Mar 2024 00:21:39 +0000 Subject: [PATCH 018/356] Merge branch 'upgrade-wko-in-aks-sample' into 'main' Update aks samples See merge request weblogic-cloud/weblogic-kubernetes-operator!4640 (cherry picked from commit a2320a0b5381976dad021f9119fe31d9c1b695f8) 108e3f40 update aks samples. b75009e6 update download-wls-tools.txt e225387d fix app path 262b221f fix scripts e8386a47 access the built in sample application. 275aca51 update index.jsp content. e39b3811 Merge branch 'oracle:main' into main e5744b7a On branch upgrade-wko-in-aks-sample Ready for review from Rosemary. --- .../azure-kubernetes-service/domain-on-pv.md | 308 +++++++++--------- .../includes/aks-connect-acr.txt | 8 + .../includes/create-acr.txt | 24 ++ .../includes/create-aks-cluster-storage.txt | 5 +- .../includes/download-samples-zip.txt | 4 +- .../includes/download-wls-tools.txt | 28 ++ .../includes/prerequisites-01.txt | 4 +- .../includes/prerequisites-02.txt | 2 +- .../includes/staging-model-files.txt | 59 ++++ .../model-in-image.md | 210 +++--------- .../create-domain-on-aks-inputs.yaml | 100 ------ .../create-domain-on-aks.sh | 11 +- 12 files changed, 329 insertions(+), 434 deletions(-) create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/download-wls-tools.txt create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/staging-model-files.txt delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.yaml diff --git a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md index 46490679509..4c2284fd981 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md +++ b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md @@ -11,19 +11,19 @@ This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://o - [Prerequisites](#prerequisites) - [Prepare Parameters](#prepare-parameters) - - [Clone WKO repository](#clone-wko-repository) - [Create Resource Group](#create-resource-group) - [Create an AKS cluster](#create-the-aks-cluster) - - [Create and Configure Storage](#create-storage) + - [Create and configure storage](#create-storage) - [Create an Azure Storage account and NFS share](#create-an-azure-storage-account-and-nfs-share) - [Create SC and PVC](#create-sc-and-pvc) + - [Create a domain creation image](#create-a-domain-creation-image) - [Install WebLogic Kubernetes Operator](#install-weblogic-kubernetes-operator-into-the-aks-cluster) - [Create WebLogic domain](#create-weblogic-domain) - [Create secrets](#create-secrets) - [Create WebLogic Domain](#create-weblogic-domain-1) - [Create LoadBalancer](#create-loadbalancer) - [Automation](#automation) - - [Deploy sample application](#deploy-sample-application) + - [Access sample application](#access-sample-application) - [Validate NFS volume](#validate-nfs-volume) - [Clean up resources](#clean-up-resources) - [Troubleshooting](#troubleshooting) @@ -84,7 +84,7 @@ export AKS_PERS_LOCATION=eastus export AKS_PERS_STORAGE_ACCOUNT_NAME="${NAME_PREFIX}storage${TIMESTAMP}" export AKS_PERS_SHARE_NAME="${NAME_PREFIX}-weblogic-${TIMESTAMP}" export SECRET_NAME_DOCKER="${NAME_PREFIX}regcred" -export ACR_ACCOUNT_NAME="${NAME_PREFIX}acr${TIMESTAMP}" +export ACR_NAME="${NAME_PREFIX}acr${TIMESTAMP}" ``` @@ -98,60 +98,139 @@ export ACR_ACCOUNT_NAME="${NAME_PREFIX}acr${TIMESTAMP}" {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt" >}} -#### Create the Azure Container Registry and connect it to the AKS cluster +#### Create a domain creation image -Your AKS cluster must be connected to a container registry so it can pull and interact with container images. The WebLogic Kubernetes Operator assumes that the docker images in the container registry have the correct structure so they are ready to run as WebLogic Docker images. The WebLogic Image Toolkit you used when satisfying the preconditions produces images that meet this requirement. In particular the image `wdt-domain-image:WLS-v1`. The steps in this section show you how to create an Azure Container Registry, connect it to your existing AKS cluster, and push the `wdt-domain-image:WLS-v1` to this registry. +This sample requires [Domain creation images]({{< relref "/managing-domains/domain-on-pv/domain-creation-images" >}}). For more information, see [Domain on Persistent Volume]({{< relref "/managing-domains/domain-on-pv/overview" >}}). -Create the Azure Container Registry in your existing resource group. + - [Image creation prerequisites](#image-creation-prerequisites) + - [Image creation - Introduction](#image-creation---introduction) + - [Understanding your first archive](#understanding-your-first-archive) + - [Staging a ZIP file of the archive](#staging-a-zip-file-of-the-archive) + - [Staging model files](#staging-model-files) + - [Creating the image with WIT](#creating-the-image-with-wit) + - [Pushing the image to Azure Container Registry](#pushing-the-image-to-azure-container-registry) + +##### Image creation prerequisites + +- The `JAVA_HOME` environment variable must be set and must reference a valid JDK 8 or 11 installation. +- Copy the sample to a new directory; for example, use the directory `/tmp/dpv-sample`. In the directory name, `dpv` is short for "domain on pv". Domain on PV is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). + + ```shell + $ rm /tmp/dpv-sample -f -r + $ mkdir /tmp/dpv-sample + ``` + + ```shell + $ cp -r $BASE_DIR/sample-scripts/create-weblogic-domain/domain-on-pv/* /tmp/dpv-sample + ``` + + **NOTE**: We will refer to this working copy of the sample as `/tmp/dpv-sample`; however, you can use a different location. +- Copy the `wdt-artifacts` directory of the sample to a new directory; for example, use directory `/tmp/dpv-sample/wdt-artifacts` + + ```shell + $ cp -r $BASE_DIR/sample-scripts/create-weblogic-domain/wdt-artifacts/* /tmp/dpv-sample + ``` + + ```shell + $ export WDT_MODEL_FILES_PATH=/tmp/dpv-sample/wdt-model-files + ``` + +{{< readfile file="/samples/azure-kubernetes-service/includes/download-wls-tools.txt" >}} + +##### Image creation - Introduction + +The goal of image creation is to demonstrate using the WebLogic Image Tool to create an image tagged as `wdt-domain-image:WLS-v1` from files that you will stage to `${WDT_MODEL_FILES_PATH}/WLS-v1`. + + - The directory where the WebLogic Deploy Tooling software is installed (also known as WDT Home), expected in an image’s `/auxiliary/weblogic-deploy` directory, by default. + - WDT model YAML (model), WDT variable (property), and WDT archive ZIP (archive) files, expected in directory `/auxiliary/models`, by default. + +##### Understanding your first archive + +See [Understanding your first archive]({{< relref "/samples/domains/domain-home-on-pv/build-domain-creation-image#understand-your-first-archive" >}}). + +##### Staging a ZIP file of the archive + +Delete existing archive.zip in case we have an old leftover version. ```shell -az acr create --resource-group $AKS_PERS_RESOURCE_GROUP --name ${ACR_ACCOUNT_NAME} --sku Basic --admin-enabled +$ rm -f ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip ``` -Successful output will be a JSON object that includes the property. +Create a ZIP file of the archive in the location that we will use when we run the WebLogic Image Tool. -```json -"id": "/subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries/" +```shell +$ cd /tmp/dpv-sample/archives/archive-v1 +$ zip -r ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip wlsdeploy ``` -Obtain the credentials to the Azure Container Registry and perform the `docker login`. +##### Staging model files + +{{< readfile file="/samples/azure-kubernetes-service/includes/staging-model-files.txt" >}} + +An image can contain multiple properties files, archive ZIP files, and model YAML files but in this sample you use just one of each. For a complete description of WDT model file naming conventions, file loading order, and macro syntax, see [Model files]({{< relref "/managing-domains/domain-on-pv/model-files" >}}) in the user documentation. + +##### Creating the image with WIT + +At this point, you have all of the files needed for `image wdt-domain-image:WLS-v1` staged; they include: + + - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/model.10.yaml` + - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/model.10.properties` + - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/archive.zip` + +Now, you use the Image Tool to create an image named `wdt-domain-image:WLS-v1`. You’ve already set up this tool during the prerequisite steps. + +Run the following commands to create the image and verify that it worked. Note that `amagetool.sh` is not supported on macOS with Apple Silicon. See [Troubleshooting - exec format error]({{< relref "/samples/azure-kubernetes-service/troubleshooting#exec-weblogic-operatorscriptsintrospectdomainsh-exec-format-error" >}}). ```shell -export LOGIN_SERVER=$(az acr show \ - --name ${ACR_ACCOUNT_NAME} \ - --query 'loginServer' \ - --output tsv) -export USER_NAME=$(az acr credential show \ - --name ${ACR_ACCOUNT_NAME} \ - --query 'username' \ - --output tsv) -export PASSWORD=$(az acr credential show \ - --name ${ACR_ACCOUNT_NAME} \ - --query 'passwords[0].value' \ - --output tsv) - -docker login $LOGIN_SERVER -u $USER_NAME -p $PASSWORD +$ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh createAuxImage \ + --tag wdt-domain-image:WLS-v1 \ + --wdtModel ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.yaml \ + --wdtVariables ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.properties \ + --wdtArchive ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip ``` -Push the `wdt-domain-image:WLS-v1` image created while satisfying the preconditions to this registry. +This command runs the WebLogic Image Tool to create the domain creation image and does the following: -```shell -docker push ${LOGIN_SERVER}/wdt-domain-image:WLS-v1 + - Builds the final container image as a layer on a small `busybox` base image. + - Copies the WDT ZIP file that's referenced in the WIT cache into the image. + - Note that you cached WDT in WIT using the keyword `latest` when you set up the cache during the sample prerequisites steps. + - This lets WIT implicitly assume it's the desired WDT version and removes the need to pass a `-wdtVersion` flag. + - Copies the specified WDT model, properties, and application archives to image location `/auxiliary/models`. + +When the command succeeds, it should end with output like the following: + +``` +[INFO ] Build successful. Build time=70s. Image tag=wdt-domain-image:WLS-v1 ``` -Set an environment variable for use in a later script. +Verify the image is available in the local Docker server with the following command. ```shell -# An example of Domain_Creation_Image_tag: xxx.azurecr.io/wdt-domain-image:WLS-v1 -export Domain_Creation_Image_tag=${LOGIN_SERVER}/wdt-domain-image:WLS-v1 +$ docker images | grep WLS-v1 +``` +``` +wdt-domain-image WLS-v1 012d3bfa3536 5 days ago 1.13GB ``` -Connect the Azure Container Registry to your existing AKS cluster. +{{% notice note %}} +You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled, see [Troubleshooting - WebLogic Image Tool failure]({{< relref "/samples/azure-kubernetes-service/troubleshooting#weblogic-image-tool-failure" >}}). +{{% /notice %}} + +##### Pushing the image to Azure Container Registry + +{{< readfile file="/samples/azure-kubernetes-service/includes/create-acr.txt" >}} + +Push the `wdt-domain-image:WLS-v1` image created while satisfying the preconditions to this registry. ```shell -az aks update --name ${AKS_CLUSTER_NAME} --resource-group $AKS_PERS_RESOURCE_GROUP --attach-acr ${ACR_ACCOUNT_NAME} +$ docker tag wdt-domain-image:WLS-v1 $LOGIN_SERVER/wdt-domain-image:WLS-v1 +$ docker push ${LOGIN_SERVER}/wdt-domain-image:WLS-v1 ``` +{{< readfile file="/samples/azure-kubernetes-service/includes/aks-connect-acr.txt" >}} + +If you see an error that seems related to you not being an **Owner on this subscription**, please refer to the troubleshooting section [Cannot attach ACR due to not being Owner of subscription]({{< relref "/samples/azure-kubernetes-service/troubleshooting#cannot-attach-acr-due-to-not-being-owner-of-subscription" >}}). + #### Install WebLogic Kubernetes Operator into the AKS cluster The WebLogic Kubernetes Operator is an adapter to integrate WebLogic Server and Kubernetes, allowing Kubernetes to serve as a container infrastructure hosting WLS instances. The operator runs as a Kubernetes Pod and stands ready to perform actions related to running WLS on Kubernetes. @@ -255,6 +334,12 @@ Now, you deploy a `sample-domain1` domain resource and an associated `sample-dom - Run the following command to generate resource files. + Export `Domain_Creation_Image_tag`, which will be referred in `create-domain-on-aks-generate-yaml.sh`. + + ```shell + export Domain_Creation_Image_tag=${LOGIN_SERVER}/wdt-domain-image:WLS-v1 + ``` + ```shell cd $BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service @@ -313,7 +398,6 @@ The domain resource references the cluster resource, a WebLogic Server installat domain1-managed-server2 1/1 Running 0 10m weblogic-operator-7796bc7b8-qmhzw 1/1 Running 0 48m weblogic-operator-webhook-b5b586bc5-ksfg9 1/1 Running 0 48m - ``` {{% notice tip %}} If Kubernetes advertises the WebLogic pod as `Running` you can be assured the WebLogic Server actually is running because the operator ensures that the Kubernetes health checks are actually polling the WebLogic health check mechanism. @@ -352,18 +436,18 @@ The domain resource references the cluster resource, a WebLogic Server installat $ kubectl get events --sort-by='.metadata.creationTimestamp' ``` -To deploy a sample application on WLS, you may skip to the section [Deploy sample application](#deploy-sample-application). The next section includes a script that automates all of the preceding steps. +To access the sample application on WLS, you may skip to the section [Access sample application](#access-sample-application). The next section includes a script that automates all of the preceding steps. #### Automation -If you want to automate the above steps of creating AKS cluster and WLS domain, you can use the script `kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh`. +If you want to automate the above steps of creating AKS cluster and WLS domain, you can use the script `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh`. The sample script will create a WLS domain home on the AKS cluster, including: - Creating a new Azure resource group, with a new Azure Storage Account and Azure File Share to allow WebLogic to persist its configuration and data separately from the Kubernetes pods that run WLS workloads. - Creating WLS domain home. - Generating the domain resource YAML files, which can be used to restart the Kubernetes artifacts of the corresponding domain. -For input values, you can edit `kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh` directly. The following values must be specified: +For input values, you can edit `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh` directly. The following values must be specified: | Name in YAML file | Example value | Notes | |-------------------|---------------------|------------------------------------------------------------------------------------------------| @@ -372,8 +456,8 @@ For input values, you can edit `kubernetes/samples/scripts/create-weblogic-domai | `weblogicUserName` | `weblogic` | Uername for WebLogic user account. | | `weblogicAccountPassword` | `Secret123456` | Password for WebLogic user account. | -``` -cd kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service +```shell +$ cd ${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service ``` ```shell @@ -386,144 +470,50 @@ To interact with the cluster using `kubectl`, use `az aks get-credentials` as sh {{% notice info %}} You now have created an AKS cluster with Azure Files NFS share to contain the WLS domain configuration files. Using those artifacts, you have used the operator to create a WLS domain. {{% /notice %}} -#### Deploy sample application +#### Access sample application -Now that you have WLS running in AKS, you can test the cluster by deploying the simple sample application included in the repository. - -First, package the application with the following command: +Access the Administration Console using the admin load balancer IP address. ```shell -$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/archive/refs/tags/v4.1.7.zip -o ${BASE_DIR}/v4.1.7.zip -$ unzip v4.1.7.zip "weblogic-kubernetes-operator-4.1.7/integration-tests/src/test/resources/bash-scripts/build-war-app.sh" "weblogic-kubernetes-operator-4.1.7/integration-tests/src/test/resources/apps/testwebapp/*" -$ cd $BASE_DIR/weblogic-kubernetes-operator-4.1.7/integration-tests/src/test/resources/bash-scripts -$ bash build-war-app.sh -s ../apps/testwebapp/ -d /tmp/testwebapp -``` - -Successful output will look similar to the following: - -```text -Found source at ../apps/testwebapp/ -build /tmp/testwebapp/testwebapp.war with command jar -cvf /tmp/testwebapp/testwebapp.war * -added manifest -ignoring entry META-INF/ -ignoring entry META-INF/MANIFEST.MF -adding: META-INF/maven/(in = 0) (out= 0)(stored 0%) -adding: META-INF/maven/com.oracle.weblogic/(in = 0) (out= 0)(stored 0%) -adding: META-INF/maven/com.oracle.weblogic/testwebapp/(in = 0) (out= 0)(stored 0%) -adding: META-INF/maven/com.oracle.weblogic/testwebapp/pom.properties(in = 117) (out= 113)(deflated 3%) -adding: META-INF/maven/com.oracle.weblogic/testwebapp/pom.xml(in = 1210) (out= 443)(deflated 63%) -adding: WEB-INF/(in = 0) (out= 0)(stored 0%) -adding: WEB-INF/web.xml(in = 951) (out= 428)(deflated 54%) -adding: WEB-INF/weblogic.xml(in = 1140) (out= 468)(deflated 58%) -adding: index.jsp(in = 1001) (out= 459)(deflated 54%) --rw-r--r-- 1 user user 3528 Jul 5 14:25 /tmp/testwebapp/testwebapp.war -``` - -Now, you are able to deploy the sample application in `/tmp/testwebapp/testwebapp.war` to the cluster. This sample uses WLS RESTful API [/management/weblogic/latest/edit/appDeployments](https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/wlrer/op-management-weblogic-version-edit-appdeployments-x-operations-1.html) to deploy the sample application. -Replace `${WEBLOGIC_USERNAME}` and `${WEBLOGIC_PASSWORD}` with the values you specified in [Create secrets](#create-secrets) or [Automation](#automation): - -```bash $ ADMIN_SERVER_IP=$(kubectl get svc domain1-admin-server-external-lb -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') -$ curl --user ${WEBLOGIC_USERNAME}:${WEBLOGIC_PASSWORD} -H X-Requested-By:MyClient -H Accept:application/json -s -v \ - -H Content-Type:multipart/form-data \ - -F "model={ - name: 'testwebapp', - targets: [ { identity: [ 'clusters', 'cluster-1' ] } ] - }" \ - -F "sourcePath=@/tmp/testwebapp/testwebapp.war" \ - -H "Prefer:respond-async" \ - -X POST http://${ADMIN_SERVER_IP}:7001/management/weblogic/latest/edit/appDeployments +$ echo "Administration Console Address: http://${ADMIN_SERVER_IP}:7001/console/" ``` -After the successful deployment, you will find output similar to the following: - -{{%expand "Click here to view the output." %}} -```text -* Trying 52.226.101.43:7001... -* TCP_NODELAY set -* Connected to 52.226.101.43 (52.226.101.43) port 7001 (#0) -* Server auth using Basic with user 'weblogic' -> POST /management/weblogic/latest/edit/appDeployments HTTP/1.1 -> Host: 52.226.101.43:7001 -> Authorization: Basic ...= -> User-Agent: curl/7.68.0 -> X-Requested-By:MyClient -> Accept:application/json -> Prefer:respond-async -> Content-Length: 3925 -> Content-Type: multipart/form-data; boundary=------------------------cc76a2c2d819911f -> Expect: 100-continue -> -* Mark bundle as not supporting multiuse -< HTTP/1.1 100 Continue -* We are completely uploaded and fine -* Mark bundle as not supporting multiuse -< HTTP/1.1 202 Accepted -< Date: Thu, 11 Aug 2022 08:32:56 GMT -< Location: http://domain1-admin-server:7001/management/weblogic/latest/domainRuntime/deploymentManager/deploymentProgressObjects/testwebapp -< Content-Length: 764 -< Content-Type: application/json -< X-ORACLE-DMS-ECID: 6f205c83-e172-4c34-a638-7f0c6345ce45-00000055 -< X-ORACLE-DMS-RID: 0 -< Set-Cookie: JSESSIONID=NOCMCQBO7dxyA2lUfCYp4zSYIeFB0S3V1KRRzigmmoOUfmQmlLOh!-546081476; path=/; HttpOnly -< Vary: Accept-Encoding -< -{ - "links": [{ - "rel": "job", - "href": "http:\/\/domain1-admin-server:7001\/management\/weblogic\/latest\/domainRuntime\/deploymentManager\/deploymentProgressObjects\/testwebapp" - }], - "identity": [ - "deploymentManager", - "deploymentProgressObjects", - "testwebapp" - ], - "rootExceptions": [], - "deploymentMessages": [], - "name": "testwebapp", - "operationType": 3, - "startTimeAsLong": 1660206785965, - "state": "STATE_RUNNING", - "id": "0", - "type": "DeploymentProgressObject", - "targets": ["cluster-1"], - "applicationName": "testwebapp", - "failedTargets": [], - "progress": "processing", - "completed": false, - "intervalToPoll": 1000, - "startTime": "2022-08-11T08:33:05.965Z" -* Connection #0 to host 52.226.101.43 left intact -``` -{{% /expand %}} - -Now, you can go to the application through the `domain1-cluster-1-lb` external IP. +Access the sample application using the cluster load balancer IP address. ```shell $ CLUSTER_IP=$(kubectl get svc domain1-cluster-1-lb -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` -$ curl http://${CLUSTER_IP}:8001/testwebapp/ + +```shell +$ curl http://${CLUSTER_IP}:8001/myapp_war/index.jsp ``` The test application will list the server host and server IP on the output, like the following: ```html - - - - +
+*****************************************************************
+
+Hello World! This is version 'v1' of the sample JSP web-app.
+
+Welcome to WebLogic Server 'managed-server1'!
+
+  domain UID  = 'domain1'
+  domain name = 'domain1'
+
+Found 1 local cluster runtime:
+  Cluster 'cluster-1'
 
-    
-    Test WebApp
-  
-  
+Found min threads constraint runtime named 'SampleMinThreads' with configured count: 1
 
+Found max threads constraint runtime named 'SampleMaxThreads' with configured count: 10
 
-    
  • InetAddress: domain1-managed-server1/10.244.1.8 -
  • InetAddress.hostname: domain1-managed-server1 +Found 0 local data sources: - - +***************************************************************** +
  • ``` #### Validate NFS volume diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt new file mode 100644 index 00000000000..ebf6eacc499 --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt @@ -0,0 +1,8 @@ +Finally, connect AKS to the ACR. For more details on connecting ACR to an existing AKS, see [Configure ACR integration for existing AKS clusters](https://learn.microsoft.com/en-us/azure/aks/cluster-container-registry-integration?tabs=azure-cli#configure-acr-integration-for-an-existing-aks-cluster). + +```shell +$ export ACR_ID=$(az acr show -n $ACR_NAME --resource-group $AKS_PERS_RESOURCE_GROUP --query "id" -o tsv) +$ az aks update --name $AKS_CLUSTER_NAME --resource-group $AKS_PERS_RESOURCE_GROUP --attach-acr $ACR_ID +``` + +Successful output will be a JSON object with the entry `"type": "Microsoft.ContainerService/ManagedClusters"`. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt new file mode 100644 index 00000000000..74085cd3308 --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt @@ -0,0 +1,24 @@ +AKS can pull images from any container registry, but the easiest integration is to use Azure Container Registry (ACR). In addition to simplicity, using ACR simplifies high availability and disaster recovery with features such as geo-replication. For more information, see [Geo-replication in Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-geo-replication). In this section, we will create a new Azure Container Registry, connect it to our pre-existing AKS cluster and push the image built in the preceding section to it. For complete details, see [Azure Container Registry documentation](https://learn.microsoft.com/en-us/azure/container-registry/). + +Let's create an instance of ACR in the same resource group we used for AKS. We will use the environment variables used during the steps above. For simplicity, we use the resource group name as the name of the ACR instance. + +```shell +$ az acr create --resource-group $AKS_PERS_RESOURCE_GROUP --name $ACR_NAME --sku Basic --admin-enabled true +``` + +Closely examine the JSON output from this command. Save the value of the `loginServer` property aside. It will look something like the following. + +```json +"loginServer": "contosoresourcegroup1610068510.azurecr.io", +``` + +Use this value to sign in to the ACR instance. Note that because you are signing in with the `az` cli, you do not need a password because your identity is already conveyed by having done `az login` previously. + +```shell +$ export LOGIN_SERVER=$(az acr show -n $ACR_NAME --resource-group $AKS_PERS_RESOURCE_GROUP --query "loginServer" -o tsv) +``` +```shell +$ az acr login --name $LOGIN_SERVER +``` + +Successful output will include `Login Succeeded`. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt index 724c25a879d..3af942c79b9 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt @@ -34,11 +34,10 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha Successful output will be a JSON object with the entry `"type": "Microsoft.Storage/storageAccounts"`. 2. Create an NFS share. - {{% notice note %}} We strongly recommend NFS instead of SMB. NFS evolved from the UNIX operating system, and other variants such as GNU/Linux. For this reason, when using NFS with container technologies such as Docker, it is less likely to have problems for concurrent reads and file locking. - Please be sure to enable NFS v4.1. Versions lower than v4.1 will have problems. + We strongly recommend NFS instead of SMB. NFS evolved from the UNIX operating system, and other variants such as GNU/Linux. For this reason, when using NFS with container technologies such as Docker, it is less likely to have problems for concurrent reads and file locking. - {{% /notice %}} + Please be sure to enable NFS v4.1. Versions lower than v4.1 will have problems. To create the file share, you must use `NoRootSquash` to allow the operator to change the ownership of the directory in the NFS share. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt index 0623bd2541b..47538eb49d7 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt @@ -1,11 +1,11 @@ ##### Download the WebLogic Kubernetes Operator sample. -Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.1.7, but should work with the latest release. +Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.1.8, but should work with the latest release. ```shell $ cd $BASE_DIR $ mkdir sample-scripts -$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/releases/download/v4.1.7/sample-scripts.zip \ +$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/releases/download/v4.1.8/sample-scripts.zip \ -o ${BASE_DIR}/sample-scripts/sample-scripts.zip $ unzip ${BASE_DIR}/sample-scripts/sample-scripts.zip -d ${BASE_DIR}/sample-scripts ``` diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/download-wls-tools.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/download-wls-tools.txt new file mode 100644 index 00000000000..38f197a3124 --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/download-wls-tools.txt @@ -0,0 +1,28 @@ +- Download the latest WebLogic Deploying Tooling (WDT) and WebLogic Image Tool (WIT) installer ZIP files to your `${WDT_MODEL_FILES_PATH}` directory. Both WDT and WIT are required to create your Model in Image images. + + ```shell + $ curl -m 120 -fL https://github.com/oracle/weblogic-deploy-tooling/releases/latest/download/weblogic-deploy.zip \ + -o ${WDT_MODEL_FILES_PATH}/weblogic-deploy.zip + ``` + ```shell + $ curl -m 120 -fL https://github.com/oracle/weblogic-image-tool/releases/latest/download/imagetool.zip \ + -o ${WDT_MODEL_FILES_PATH}/imagetool.zip + ``` + +- Set up the WebLogic Image Tool, run the following commands: + ```shell + $ unzip ${WDT_MODEL_FILES_PATH}/imagetool.zip -d ${WDT_MODEL_FILES_PATH} + ``` + + ``` + $ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh cache deleteEntry --key wdt_latest + ``` + + ```shell + $ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh cache addInstaller \ + --type wdt \ + --version latest \ + --path ${WDT_MODEL_FILES_PATH}/weblogic-deploy.zip + ``` + + These steps will install WIT to the `${WDT_MODEL_FILES_PATH}/imagetool` directory, plus put a `wdt_latest` entry in the tool’s cache which points to the WDT ZIP file installer. You will use WIT later in the sample for creating model images. \ No newline at end of file diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt index 9b90a5902fa..0024b3be44b 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt @@ -10,9 +10,9 @@ This sample assumes the following prerequisite environment. * Operating System: GNU/Linux, macOS or [WSL2 for Windows 10](https://docs.microsoft.com/windows/wsl/install-win10). * Note: the Docker image creation steps will not work on a Mac with Apple Silicon. * [Git](https://git-scm.com/downloads); use `git --version` to test if `git` works. This document was tested with version 2.25.1. -* [Azure CLI](https://docs.microsoft.com/cli/azure); use `az --version` to test if `az` works. This document was tested with version 2.48.1. +* [Azure CLI](https://docs.microsoft.com/cli/azure); use `az --version` to test if `az` works. This document was tested with version 2.58.0. * [Docker for Desktop](https://www.docker.com/products/docker-desktop). This document was tested with `Docker version 20.10.7` * [kubectl](https://kubernetes-io-vnext-staging.netlify.com/docs/tasks/tools/install-kubectl/); use `kubectl version` to test if `kubectl` works. This document was tested with version v1.21.2. * [Helm](https://helm.sh/docs/intro/install/), version 3.1 and later; use `helm version` to check the `helm` version. This document was tested with version v3.6.2. * A Java JDK, Version 8 or 11. Azure recommends [Microsoft Build of OpenJDK](https://docs.microsoft.com/java/openjdk/download). Ensure that your `JAVA_HOME` environment variable is set correctly in the shells in which you run the commands. -* A `domain creation image` tagged as `wdt-domain-image:WLS-v1` in your local Docker server's image cache. You can create it by following the [Build the domain creation image]({{< relref "/samples/domains/domain-home-on-pv/build-domain-creation-image#build-the-domain-creation-image" >}}) step. +* Ensure that you have the zip/unzip utility installed; use `zip/unzip -v` to test if `zip/unzip` works. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt index 7ced36c7515..880e05a783e 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt @@ -7,7 +7,7 @@ This sample assumes the following prerequisite environment. * If your identity has very limited role assignments, ensure you have [Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#contributor) role and [User Access Administrator](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#user-access-administrator) role in the resource group that runs the AKS cluster. This requires asking a privileged user to assign the roles before creating resources in the resource group. * Operating System: GNU/Linux, macOS (Intel only, Apple Silicon not supported) [WSL2 for Windows 10](https://docs.microsoft.com/windows/wsl/install-win10). * [Git](https://git-scm.com/downloads); use `git --version` to test if `git` works. This document was tested with version 2.25.1. -* [Azure CLI](https://docs.microsoft.com/cli/azure); use `az --version` to test if `az` works. This document was tested with version 2.48.1. +* [Azure CLI](https://docs.microsoft.com/cli/azure); use `az --version` to test if `az` works. This document was tested with version 2.58.0. * [Docker for Desktop](https://www.docker.com/products/docker-desktop). This document was tested with `Docker version 20.10.7` * [kubectl](https://kubernetes-io-vnext-staging.netlify.com/docs/tasks/tools/install-kubectl/); use `kubectl version` to test if `kubectl` works. This document was tested with version v1.21.2. * [Helm](https://helm.sh/docs/intro/install/), version 3.1 and later; use `helm version` to check the `helm` version. This document was tested with version v3.6.2. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/staging-model-files.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/staging-model-files.txt new file mode 100644 index 00000000000..3a65b022ed6 --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/staging-model-files.txt @@ -0,0 +1,59 @@ +In this step, you explore the staged WDT model YAML file and properties in the `${WDT_MODEL_FILES_PATH}/WLS-v1` directory. The model in this directory references the web application in your archive, configures a WebLogic Server Administration Server, and configures a WebLogic cluster. It consists of only two files, `model.10.properties`, a file with a single property, and, `model.10.yaml`, a YAML file with your WebLogic configuration. + +Here is the WLS `model.10.properties`: + +``` +CLUSTER_SIZE=5 +``` + +Here is the WLS `model.10.yaml`: + +```yaml +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'prod' + +topology: + Name: '@@ENV:CUSTOM_DOMAIN_NAME@@' + AdminServerName: 'admin-server' + Cluster: + 'cluster-1': + DynamicServers: + ServerTemplate: 'cluster-1-template' + ServerNamePrefix: 'managed-server' + DynamicClusterSize: '@@PROP:CLUSTER_SIZE@@' + MaxDynamicClusterSize: '@@PROP:CLUSTER_SIZE@@' + MinDynamicClusterSize: '0' + CalculatedListenPorts: false + Server: + 'admin-server': + ListenPort: 7001 + ServerTemplate: + 'cluster-1-template': + Cluster: 'cluster-1' + ListenPort: 8001 + +appDeployments: + Application: + myapp: + SourcePath: 'wlsdeploy/applications/myapp-v1' + ModuleType: ear + Target: 'cluster-1' +``` + +The model file: + +- Defines a WebLogic domain with: + - Cluster `cluster-1` + - Administration Server `admin-server` + - An EAR application, targeted to `cluster-1`, located in the WDT archive ZIP file at `wlsdeploy/applications/myapp-v1` + +- Leverages macros to inject external values: + - The property file `CLUSTER_SIZE` property is referenced in the model YAML file `DynamicClusterSize` and `MaxDynamicClusterSize` fields using a PROP macro. + - The model file domain name is injected using a custom environment variable named `CUSTOM_DOMAIN_NAME` using an ENV macro. + - You set this environment variable later in this sample using an `env` field in its Domain. + - _This conveniently provides a simple way to deploy multiple differently named domains using the same model image_. + - The model file administrator user name and password are set using a `weblogic-credentials` secret macro reference to the WebLogic credential secret. + - This secret is in turn referenced using the `webLogicCredentialsSecret` field in the Domain. + - The `weblogic-credentials` is a reserved name that always dereferences to the owning Domain actual WebLogic credentials secret name. \ No newline at end of file diff --git a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md index b9d855f3b69..3064df74ca8 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md +++ b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md @@ -88,18 +88,18 @@ sample-weblogic-operator-sa 1 9m5s Install the operator. The operator’s Helm chart is located in the kubernetes/charts/weblogic-operator directory. This sample installs the operator using Helm charts from Github. It may take you several minutes to install the operator. -``` -helm repo add weblogic-operator https://oracle.github.io/weblogic-kubernetes-operator/charts --force-update +```shell +$ helm repo add weblogic-operator https://oracle.github.io/weblogic-kubernetes-operator/charts --force-update ``` Update the repo to get the latest Helm charts. It is a best practice to do this every time before installing a new operator version. In this example, we are using a pinned version, but you may also find success if you use the latest version. In this case, you can omit the `--version` argument. Be warned that these instructions have only been tested with the exact version shown. -``` +```shell $ helm repo update $ helm install weblogic-operator weblogic-operator/weblogic-operator \ --namespace sample-weblogic-operator-ns \ - --version 4.1.7 \ + --version 4.1.8 \ --set serviceAccount=sample-weblogic-operator-sa \ --wait ``` @@ -115,7 +115,7 @@ REVISION: 1 TEST SUITE: None ``` -{{% notice tip %}} If you wish to use a more recent version of the operator, replace the `4.1.7` in the preceding command with the other version number. To see the list of versions, visit the [GitHub releases page](https://github.com/oracle/weblogic-kubernetes-operator/releases). +{{% notice tip %}} If you wish to use a more recent version of the operator, replace the `4.1.8` in the preceding command with the other version number. To see the list of versions, visit the [GitHub releases page](https://github.com/oracle/weblogic-kubernetes-operator/releases). {{% /notice %}} @@ -126,7 +126,7 @@ $ helm list -A ``` ``` NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -weblogic-operator sample-weblogic-operator-ns 1 2023-05-15 10:31:05.1890341 +0800 CST deployeweblogic-operator-4.1.7 4.1.7 +weblogic-operator sample-weblogic-operator-ns 1 2023-05-15 10:31:05.1890341 +0800 CST deployeweblogic-operator-4.1.8 4.1.8 ``` ```shell $ kubectl get pods -n sample-weblogic-operator-ns @@ -156,8 +156,9 @@ If you have an image built with domain models following [Model in Image]({{< rel - [Pushing the image to Azure Container Registry](#pushing-the-image-to-azure-container-registry) ##### Image creation prerequisites -1. The `JAVA_HOME` environment variable must be set and must reference a valid JDK 8 or 11 installation. -1. Copy the sample to a new directory; for example, use the directory `/tmp/mii-sample`. In the directory name, `mii` is short for "model in image". Model in image is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). + +- The `JAVA_HOME` environment variable must be set and must reference a valid JDK 8 or 11 installation. +- Copy the sample to a new directory; for example, use the directory `/tmp/mii-sample`. In the directory name, `mii` is short for "model in image". Model in image is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). ```shell $ mkdir /tmp/mii-sample @@ -167,35 +168,16 @@ If you have an image built with domain models following [Model in Image]({{< rel $ cp -r $BASE_DIR/sample-scripts/create-weblogic-domain/wdt-artifacts/* /tmp/mii-sample ``` - **NOTE**: We will refer to this working copy of the sample as `/tmp/mii-sample`; however, you can use a different location. - -1. Download the latest WebLogic Deploying Tooling (WDT) and WebLogic Image Tool (WIT) installer ZIP files to your `/tmp/mii-sample/wdt-model-files` directory. Both WDT and WIT are required to create your Model in Image images. + Save the model file directory. ```shell - export WDT_MODEL_FILES_PATH=/tmp/mii-sample/wdt-model-files + $ export WDT_MODEL_FILES_PATH=/tmp/mii-sample/wdt-model-files ``` - ```shell - $ curl -m 120 -fL https://github.com/oracle/weblogic-deploy-tooling/releases/latest/download/weblogic-deploy.zip \ - -o ${WDT_MODEL_FILES_PATH}/weblogic-deploy.zip - ``` - ```shell - $ curl -m 120 -fL https://github.com/oracle/weblogic-image-tool/releases/latest/download/imagetool.zip \ - -o ${WDT_MODEL_FILES_PATH}/imagetool.zip - ``` - - To set up the WebLogic Image Tool, run the following commands: - ```shell - $ unzip ${WDT_MODEL_FILES_PATH}/imagetool.zip -d ${WDT_MODEL_FILES_PATH} - ``` - ```shell - $ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh cache addInstaller \ - --type wdt \ - --version latest \ - --path ${WDT_MODEL_FILES_PATH}/weblogic-deploy.zip - ``` + **NOTE**: We will refer to this working copy of the sample as `/tmp/mii-sample`; however, you can use a different location. + +{{< readfile file="/samples/azure-kubernetes-service/includes/download-wls-tools.txt" >}} - These steps will install WIT to the `${WDT_MODEL_FILES_PATH}/imagetool` directory, plus put a `wdt_latest` entry in the tool’s cache which points to the WDT ZIP file installer. You will use WIT later in the sample for creating model images. ##### Image creation - Introduction @@ -243,65 +225,7 @@ $ zip -r ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip wlsdeploy ##### Staging model files -In this step, you explore the staged WDT model YAML file and properties in the `/tmp/mii-sample/model-in-image__WLS-v1` directory. The model in this directory references the web application in your archive, configures a WebLogic Server Administration Server, and configures a WebLogic cluster. It consists of only two files, `model.10.properties`, a file with a single property, and, `model.10.yaml`, a YAML file with your WebLogic configuration. - -Here is the WLS `model.10.properties`: - -``` -CLUSTER_SIZE=5 -``` - -Here is the WLS `model.10.yaml`: - -```yaml -domainInfo: - AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' - AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' - ServerStartMode: 'prod' - -topology: - Name: '@@ENV:CUSTOM_DOMAIN_NAME@@' - AdminServerName: 'admin-server' - Cluster: - 'cluster-1': - DynamicServers: - ServerTemplate: 'cluster-1-template' - ServerNamePrefix: 'managed-server' - DynamicClusterSize: '@@PROP:CLUSTER_SIZE@@' - MaxDynamicClusterSize: '@@PROP:CLUSTER_SIZE@@' - MinDynamicClusterSize: '0' - CalculatedListenPorts: false - Server: - 'admin-server': - ListenPort: 7001 - ServerTemplate: - 'cluster-1-template': - Cluster: 'cluster-1' - ListenPort: 8001 - -appDeployments: - Application: - myapp: - SourcePath: 'wlsdeploy/applications/myapp-v1' - ModuleType: ear - Target: 'cluster-1' -``` - -The model file: - -- Defines a WebLogic domain with: - - Cluster `cluster-1` - - Administration Server `admin-server` - - An EAR application, targeted to `cluster-1`, located in the WDT archive ZIP file at `wlsdeploy/applications/myapp-v1` - -- Leverages macros to inject external values: - - The property file `CLUSTER_SIZE` property is referenced in the model YAML file `DynamicClusterSize` and `MaxDynamicClusterSize` fields using a PROP macro. - - The model file domain name is injected using a custom environment variable named `CUSTOM_DOMAIN_NAME` using an ENV macro. - - You set this environment variable later in this sample using an `env` field in its Domain. - - _This conveniently provides a simple way to deploy multiple differently named domains using the same model image_. - - The model file administrator user name and password are set using a `weblogic-credentials` secret macro reference to the WebLogic credential secret. - - This secret is in turn referenced using the `webLogicCredentialsSecret` field in the Domain. - - The `weblogic-credentials` is a reserved name that always dereferences to the owning Domain actual WebLogic credentials secret name. +{{< readfile file="/samples/azure-kubernetes-service/includes/staging-model-files.txt" >}} A Model in Image image can contain multiple properties files, archive ZIP files, and YAML files but in this sample you use just one of each. For a complete description of Model in Images model file naming conventions, file loading order, and macro syntax, see [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}) files in the Model in Image user documentation. @@ -363,55 +287,25 @@ You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled ##### Pushing the image to Azure Container Registry -AKS can pull images from any container registry, but the easiest integration is to use Azure Container Registry (ACR). In this section, we will create a new Azure Container Registry, connect it to our pre-existing AKS cluster and push the image built in the preceding section to it. For complete details, see [Azure Container Registry documentation](https://docs.microsoft.com/en-us/azure/container-registry/). - -Let's create an instance of ACR in the same resource group we used for AKS. We will use the environment variables used during the steps above. For simplicity, we use the resource group name as the name of the ACR instance. - -```shell -$ az acr create --resource-group $AKS_PERS_RESOURCE_GROUP --name $ACR_NAME --sku Basic --admin-enabled true -``` - -Closely examine the JSON output from this command. Save the value of the `loginServer` property aside. It will look something like the following. - -```json -"loginServer": "contosoresourcegroup1610068510.azurecr.io", -``` - -Use this value to sign in to the ACR instance. Note that because you are signing in with the `az` cli, you do not need a password because your identity is already conveyed by having done `az login` previously. - -```shell -$ export AKS_PERS_ACR=$(az acr show -n $ACR_NAME --query "loginServer" -o tsv) -``` -```shell -$ az acr login --name $AKS_PERS_ACR -``` - -Successful output will include `Login Succeeded`. +{{< readfile file="/samples/azure-kubernetes-service/includes/create-acr.txt" >}} Ensure Docker is running on your local machine. Run the following commands to tag and push the image to your ACR. ```shell -$ docker tag model-in-image:WLS-v1 $AKS_PERS_ACR/model-in-image-aks:1.0 +$ docker tag model-in-image:WLS-v1 $LOGIN_SERVER/model-in-image-aks:1.0 ``` ```shell -$ docker push $AKS_PERS_ACR/model-in-image-aks:1.0 +$ docker push $LOGIN_SERVER/model-in-image-aks:1.0 ``` ``` The push refers to repository [contosorgresourcegroup1610068510.azurecr.io/model-in-image-aks] 1.0: digest: sha256:208217afe336053e4c524caeea1a415ccc9cc73b206ee58175d0acc5a3eeddd9 size: 2415 ``` -Finally, connect AKS to the ACR. For more details on connecting ACR to an existing AKS, see [Configure ACR integration for existing AKS clusters](https://docs.microsoft.com/en-us/azure/aks/cluster-container-registry-integration#configure-acr-integration-for-existing-aks-clusters). - -```shell -$ az aks update --name $AKS_CLUSTER_NAME --resource-group $AKS_PERS_RESOURCE_GROUP --attach-acr $ACR_NAME -``` +{{< readfile file="/samples/azure-kubernetes-service/includes/aks-connect-acr.txt" >}} If you see an error that seems related to you not being an **Owner on this subscription**, please refer to the troubleshooting section [Cannot attach ACR due to not being Owner of subscription]({{< relref "/samples/azure-kubernetes-service/troubleshooting#cannot-attach-acr-due-to-not-being-owner-of-subscription" >}}). -Successful output will be a JSON object with the entry `"type": "Microsoft.ContainerService/ManagedClusters"`. - - #### Create WebLogic domain In this section, you will deploy the new image to the namespace `sample-domain1-ns`, including the following steps: @@ -501,17 +395,23 @@ $ kubectl -n sample-domain1-ns label secret \ Now, you create a domain YAML file. Think of the domain YAML file as the way to configure some aspects of your WebLogic domain using Kubernetes. The operator uses the Kubernetes "custom resource" feature to define a Kubernetes resource type called `Domain`. For more on the `Domain` Kubernetes resource, see [Domain Resource]({{< relref "/managing-domains/domain-resource" >}}). For more on custom resources see [the Kubernetes documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). -We provide a sample file at `kubernetes/samples/scripts/create-weblogic-domain/model-in-image/domain-resources/WLS-LEGACY/mii-initial-d1-WLS-LEGACY-v1.yaml`, copy it to a file called `/tmp/mii-sample/mii-initial.yaml`. +We provide a sample file at `$BASE_DIR/sample-scripts/create-weblogic-domain/model-in-image/domain-resources/WLS-LEGACY/mii-initial-d1-WLS-LEGACY-v1.yaml`, copy it to a file called `/tmp/mii-sample/mii-initial.yaml`. + +```shell +$ cp $BASE_DIR/sample-scripts/create-weblogic-domain/model-in-image/domain-resources/WLS-LEGACY/mii-initial-d1-WLS-LEGACY-v1.yaml /tmp/mii-sample/mii-initial.yaml +``` + +Print the image path. Copy the output to your clipboard and paste it to value of `spec.image` in `/tmp/mii-sample/mii-initial.yaml`. ```shell -$ cp $BASE_DIR/weblogic-kubernetes-operator/kubernetes/samples/scripts/create-weblogic-domain/model-in-image/domain-resources/WLS-LEGACY/mii-initial-d1-WLS-LEGACY-v1.yaml /tmp/mii-sample/mii-initial.yaml +echo $LOGIN_SERVER/model-in-image-aks:1.0 ``` Modify the Domain YAML with your values. | Name in YAML file | Example value | Notes | |-------------------|---------------|-------| -|`spec.image`|`$AKS_PERS_ACR/model-in-image-aks:1.0`|Must be the same as the value to which you pushed the image to by running the command `docker push $AKS_PERS_ACR/model-in-image-aks:1.0`.| +|`spec.image`|`$LOGIN_SERVER/model-in-image-aks:1.0`|Must be the same as the value to which you pushed the image to by running the command `docker push $LOGIN_SERVER/model-in-image-aks:1.0`.| Run the following command to create the domain custom resource: @@ -577,7 +477,7 @@ If the system does not reach this state, troubleshoot and resolve the problem be Create the Azure public standard load balancer to access the WebLogic Server Administration Console and applications deployed in the cluster. -Use the configuration file in `kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml` to create a load balancer service for the Administration Server. If you are choosing not to use the predefined YAML file and instead created a new one with customized values, then substitute the following content with you domain values. +Use the configuration file in `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml` to create a load balancer service for the Administration Server. If you are choosing not to use the predefined YAML file and instead created a new one with customized values, then substitute the following content with you domain values. {{%expand "Click here to view YAML content." %}} ```yaml @@ -600,7 +500,7 @@ spec: ``` {{% /expand %}} -Use the configuration file in `kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml` to create a load balancer service for the managed servers. If you are choosing not to use the predefined YAML file and instead created new one with customized values, then substitute the following content with you domain values. +Use the configuration file in `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml` to create a load balancer service for the managed servers. If you are choosing not to use the predefined YAML file and instead created new one with customized values, then substitute the following content with you domain values. {{%expand "Click here to view YAML content." %}} ```yaml @@ -627,13 +527,13 @@ spec: Create the load balancer services using the following command: ```shell -$ kubectl apply -f $BASE_DIR/weblogic-kubernetes-operator/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml +$ kubectl apply -f $BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml ``` ``` service/sample-domain1-admin-server-external-lb created ``` ```shell -$ kubectl apply -f $BASE_DIR/weblogic-kubernetes-operator/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml +$ kubectl apply -f $BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml ``` ``` service/sample-domain1-cluster-1-external-lb created @@ -793,53 +693,41 @@ Events: ##### Access the application -Access the Administration Console using the admin load balancer IP address, `http://52.191.234.149:7001/console` +Access the Administration Console using the admin load balancer IP address. + +```shell +$ ADMIN_SERVER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-admin-server-external-lb -o=jsonpath='{.status.loadBalancer.ingress\[0\].ip}') +$ echo "Administration Console Address: http://${ADMIN_SERVER_IP}:7001/console/" +``` Access the sample application using the cluster load balancer IP address. +```shell +## Access the sample application using the cluster load balancer IP. +$ CLUSTER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-cluster-1-lb -o=jsonpath='{.status.loadBalancer.ingress\[0\].ip}') ``` -## Access the sample application using the cluster load balancer IP (52.191.235.71) -``` -``` -$ CLUSTER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-cluster-1-lb -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') + +```shell $ curl http://${CLUSTER_IP}:8001/myapp_war/index.jsp ``` + ```
     *****************************************************************
     
    -Hello World! This is version 'v1' of the mii-sample JSP web-app.
    +Hello World! This is version 'v1' of the sample JSP web-app.
     
     Welcome to WebLogic Server 'managed-server1'!
     
    - domain UID  = 'sample-domain1'
    - domain name = 'domain1'
    +  domain UID  = 'sample-domain1'
    +  domain name = 'domain1'
     
     Found 1 local cluster runtime:
       Cluster 'cluster-1'
     
    -Found 0 local data sources:
    -
    -*****************************************************************
    -
    - -``` -```shell -$ curl http://52.191.235.71:8001/myapp_war/index.jsp -``` -``` -
    -*****************************************************************
    -
    -Hello World! This is version 'v1' of the mii-sample JSP web-app.
    +Found min threads constraint runtime named 'SampleMinThreads' with configured count: 1
     
    -Welcome to WebLogic Server 'managed-server2'!
    -
    - domain UID  = 'sample-domain1'
    - domain name = 'domain1'
    -
    -Found 1 local cluster runtime:
    -  Cluster 'cluster-1'
    +Found max threads constraint runtime named 'SampleMaxThreads' with configured count: 10
     
     Found 0 local data sources:
     
    diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.yaml b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.yaml
    deleted file mode 100644
    index 6ea919ba97a..00000000000
    --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.yaml
    +++ /dev/null
    @@ -1,100 +0,0 @@
    -# Copyright (c) 2018, 2021, Oracle and/or its affiliates.
    -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
    -
    -# The version of this inputs file. Do not modify.
    -version: create-domain-on-aks-inputs-v1
    -
    -#
    -# Parameters that must be changed from these values!
    -#
    -
    -# Oracle Single Sign-On (SSO) account email, used to pull the WebLogic Server image.
    -dockerEmail: docker-email
    -
    -# Oracle SSO account password, used to pull the WebLogic Server image.
    -dockerPassword: docker-password
    -
    -# The same value as dockerEmail.
    -dockerUserName: docker-user-name
    -
    -# Specify where to create azure resource.
    -azureLocation: eastus
    -
    -# Specify a prefix to name resources, only allow lowercase letters and numbers, between 1 and 7 characters.
    -# Resource group is named with ${namePrefix}resourcegroup, e.g. wlsresourcegroup1592469388
    -# Kubernetes cluster is named with ${namePrefix}akscluster, e.g. wlsakscluster1592469388
    -# Storage account is named with ${namePrefix}storage, e.g. wlsstorage1592469388
    -namePrefix: wls
    -
    -#
    -# Parameters that may optionally be changed.
    -#
    -
    -# The suffix of file share secret name, the complete value is ${namePrefix}${azureFileShareSecretNameSuffix}.
    -azureFileShareSecretNameSuffix: azure-secret
    -
    -# Number of azure kubernetes nodes, used to create azure kubernetes cluster.
    -azureKubernetesNodeCount: 2
    -
    -# VM size of azure kubernetes node.
    -azureKubernetesNodeVMSize: Standard_DS2_v2
    -
    -# The suffix of azure kubernetes node pool name, the azure kubernetes node pool name will be${azureKubernetesNodepoolNamePrefix} ${namePrefix}.
    -azureKubernetesNodepoolNamePrefix: pool1
    -
    -#Java Option for WebLogic Server
    -javaOptions: -Dweblogic.StdoutDebugEnabled=false -XX:InitialRAMPercentage=25.0 -XX:MaxRAMPercentage=50.0
    -
    -# The suffix of the Kubernetes secret name, the complete value is ${namePrefix}${imagePullSecretNameSuffix}. The secret name is used to access the container registry to pull the WebLogic Server image
    -# Used to create Kubernetes secret for container registry account.
    -# Parameter "imagePullSecretName" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml
    -imagePullSecretNameSuffix: regcred
    -
    -# Storage class name for Azure Files using Container Storage Interface driver, see https://docs.microsoft.com/en-us/azure/aks/azure-files-csi#nfs-file-shares
    -azureFileCsiNfsClassName: azurefile-csi-nfs
    -
    -# The suffix of azure storage file share name, the complete value is ${namePrefix}-${azureStorageShareNameSuffix}-, used to create file share, and mount file share. 
    -azureStorageShareNameSuffix: weblogic
    -
    -# Resource request for each server pod (Memory and CPU). This is minimum amount of compute
    -# resources required for each server pod. Edit value(s) below as per pod sizing requirements.
    -# These are optional 
    -# Please refer to the kubernetes documentation on Managing Compute
    -# Resources for Containers for details.
    -# Parameter "serverPodMemoryRequest" and "serverPodCpuRequest" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml
    -serverPodMemoryRequest: "1.5Gi"
    -serverPodCpuRequest: "250m"
    -
    -# Uncomment and edit value(s) below to specify the maximum amount of compute resources allowed 
    -# for each server pod.
    -# These are optional. 
    -# Please refer to the kubernetes documentation on Managing Compute
    -# Resources for Containers for details.
    -# Parameter "serverPodMemoryLimit" and "serverPodCpuLimit" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml
    -serverPodMemoryLimit: "1.5Gi"
    -serverPodCpuLimit: "250m"
    -
    -# The suffix of the persistent volume claim name, the complete value is ${namePrefix}-${persistentVolumeClaimNameSuffix}-.
    -# Parameter "persistentVolumeClaimName" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml
    -persistentVolumeClaimNameSuffix: azurefile
    -
    -# Password for WebLogic account.
    -weblogicAccountPassword: weblogic-account-password
    -
    -# WebLogic Server image.
    -# Parameter "image" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml
    -# **NOTE**:
    -# This sample uses General Availability (GA) images. GA images are suitable for demonstration and
    -# development purposes only where the environments are not available from the public Internet;
    -# they are not acceptable for production use. In production, you should always use CPU (patched)
    -# images from OCR or create your images using the WebLogic Image Tool.
    -# Please refer to the `OCR` and `WebLogic Images` pages in the WebLogic Kubernetes Operator
    -# documentation for details.
    -weblogicDockerImage: container-registry.oracle.com/middleware/weblogic:12.2.1.4
    -
    -# Name of weblogic user.
    -weblogicUserName: weblogic-user-name
    -
    -
    -
    -
    diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh
    index b6f1dd17246..b7237c0298f 100755
    --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh
    +++ b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh
    @@ -10,9 +10,9 @@
     #  artifacts of the corresponding domain.
     #
     #  The Azure resource deployment is customized by editing
    -#  create-domain-on-aks-inputs.yaml. If you also want to customize
    +#  create-domain-on-aks-inputs.sh. If you also want to customize
     #  WebLogic Server domain configuration, please edit
    -#  kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml.  Or you can create a copy of this file and edit it and refer to the copy using "-d ".
    +#  kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-on-aks-inputs.sh.  Or you can create a copy of this file and edit it and refer to the copy using "-d ".
     #
     #  The following pre-requisites must be handled prior to running this script:
     #    * Environment has set up, with git, azure cli, kubectl and helm installed.
    @@ -714,11 +714,9 @@ unzip imagetool.zip
       --version latest \
       --path ${image_build_base_dir}/sample/wdt-artifacts/weblogic-deploy.zip
     
    -unzip ${image_build_base_dir}/sample/wdt-artifacts/weblogic-deploy.zip
     rm -f ${image_build_base_dir}/sample/wdt-artifacts/wdt-model-files/WLS-v1/archive.zip
     cd ${image_build_base_dir}/sample/wdt-artifacts/archives/archive-v1
    -
    -${image_build_base_dir}/sample/wdt-artifacts/weblogic-deploy/bin/archiveHelper.sh add application -archive_file=${image_build_base_dir}/sample/wdt-artifacts/wdt-model-files/WLS-v1/archive.zip -source=wlsdeploy/applications/myapp-v1
    +zip -r ${image_build_base_dir}/sample/wdt-artifacts/wdt-model-files/WLS-v1/archive.zip wlsdeploy
     
     cd ${image_build_base_dir}/sample/wdt-artifacts/wdt-model-files/WLS-v1
     ${image_build_base_dir}/sample/wdt-artifacts/imagetool/bin/imagetool.sh createAuxImage \
    @@ -743,7 +741,8 @@ docker push ${acr_account_name}.azurecr.io/wdt-domain-image:WLS-v1
     
     # allow aks to access acr
     echo allow aks to access acr
    -az aks update --name $aksClusterName --resource-group $azureResourceGroupName --attach-acr $acr_account_name
    +acr_id=$(az acr show -n $acr_account_name --resource-group $azureResourceGroupName --query "id" -o tsv)
    +az aks update --name $aksClusterName --resource-group $azureResourceGroupName --attach-acr $acr_id
     
     ## build image success
     echo "build image end----------"
    
    From 91bd9a732887103feb5c4d9558801069a984591a Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 1 Apr 2024 13:00:51 -0400
    Subject: [PATCH 019/356] Dependency updates
    
    ---
     operator-build-maven-plugin/pom.xml | 2 +-
     pom.xml                             | 6 +++---
     2 files changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml
    index 492847b32ea..4f1c74cdd4b 100644
    --- a/operator-build-maven-plugin/pom.xml
    +++ b/operator-build-maven-plugin/pom.xml
    @@ -45,7 +45,7 @@
             
                 org.ow2.asm
                 asm
    -            9.6
    +            9.7
                 test
             
         
    diff --git a/pom.xml b/pom.xml
    index 71afb308148..e0d9ac5b3cc 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -676,7 +676,7 @@
         10.14.2
         1.0
         3.3.2
    -    3.2.1
    +    3.2.2
         2.0.0.0
         1.3.3
         2.0.1
    @@ -692,7 +692,7 @@
         3.5.0
         1.0.0
         3.25.3
    -    2.15.1
    +    2.16.0
         4.2.1
         19.0.1
         3.0.1u2
    @@ -725,7 +725,7 @@
         0.8.11
         4.9.10
         2.70.0
    -    2.5.1
    +    2.7.3
         ${root.basedir}/build-tools/checkstyle/customized_google_checks.xml
         false
       
    
    From 39dc3ac80dbb6ecf745838f2d65cfab047cd321e Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 1 Apr 2024 13:31:07 -0400
    Subject: [PATCH 020/356] Dependency updates
    
    ---
     pom.xml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/pom.xml b/pom.xml
    index e0d9ac5b3cc..e39d877bc61 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -714,7 +714,7 @@
         2.17.0
         2.2
         2.10.1
    -    9.0.10
    +    9.1.0
         2.0.12
         1.5.3
         ${project.basedir}/src-generated-swagger
    
    From 98bc7e5ed8a3ea1f2a40079e4b35949b55e570ba Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Thu, 4 Apr 2024 11:50:23 -0400
    Subject: [PATCH 021/356] Dependency updates
    
    ---
     operator-build-maven-plugin/pom.xml | 4 ++--
     pom.xml                             | 4 ++--
     2 files changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml
    index 4f1c74cdd4b..af726b2e09a 100644
    --- a/operator-build-maven-plugin/pom.xml
    +++ b/operator-build-maven-plugin/pom.xml
    @@ -30,7 +30,7 @@
             
                 org.apache.maven.plugin-tools
                 maven-plugin-annotations
    -            3.11.0
    +            3.12.0
             
             
                 org.junit.jupiter
    @@ -59,7 +59,7 @@
                 
                     org.apache.maven.plugins
                     maven-plugin-plugin
    -                3.11.0
    +                3.12.0
                     
                         
                             oracle.kubernetes:jsonschema-maven-plugin
    diff --git a/pom.xml b/pom.xml
    index e39d877bc61..67fb92f550e 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -673,7 +673,7 @@
         3.6.1
         3.5.2
         3.2.0
    -    10.14.2
    +    10.15.0
         1.0
         3.3.2
         3.2.2
    @@ -722,7 +722,7 @@
         ${project.basedir}/swagger/domain.json
         false
         false
    -    0.8.11
    +    0.8.12
         4.9.10
         2.70.0
         2.7.3
    
    From 718149d6b1aeaea6e18747ee3c48eec76581dcbf Mon Sep 17 00:00:00 2001
    From: maggie_he 
    Date: Fri, 5 Apr 2024 13:03:24 +0000
    Subject: [PATCH 022/356] Backpor ItRemoteConsole podman conversion change to
     release/4.2
    
    ---
     Jenkinsfile.podman                            |  31 ++-
     .../weblogic/kubernetes/ItRemoteConsole.java  | 225 ++++++++++++------
     .../weblogic/kubernetes/TestConstants.java    |   8 +-
     3 files changed, 181 insertions(+), 83 deletions(-)
    
    diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman
    index c7c2775ccb3..cba5d194e3a 100644
    --- a/Jenkinsfile.podman
    +++ b/Jenkinsfile.podman
    @@ -182,11 +182,11 @@ pipeline {
             )
             string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION',
                    description: '',
    -               defaultValue: '2.0.7'
    +               defaultValue: '2.1.2'
             )
             string(name: 'PROMETHEUS_CHART_VERSION',
                    description: '',
    -               defaultValue: '15.2.0'
    +               defaultValue: '17.0.0'
             )
             string(name: 'GRAFANA_CHART_VERSION',
                    description: '',
    @@ -349,7 +349,7 @@ pipeline {
                                   podman stop "${registry_name}"
                                   podman rm --force "${registry_name}"
                                 fi
    -        
    +
                                 podman run -d --restart=always -p "127.0.0.1:${registry_port}:5000" --name "${registry_name}" \
                                     ${ocir_host}/${wko_tenancy}/test-images/docker/registry:2.8.2
                                 echo "Registry Host: ${registry_host}"
    @@ -394,6 +394,17 @@ nodes:
           - containerPort: 30443
             hostPort: 2443
             protocol: TCP
    +      - containerPort: 31880
    +        hostPort: 2180
    +        protocol: TCP
    +      - containerPort: 31443
    +        hostPort: 2543
    +      - containerPort: 30881
    +        hostPort: 2081
    +        protocol: TCP
    +      - containerPort: 30444
    +        hostPort: 2444
    +        protocol: TCP
           - containerPort: 32480
             hostPort: 2480
           - containerPort: 32490
    @@ -409,7 +420,19 @@ nodes:
             protocol: TCP
           - containerPort: 32170
             hostPort: 2173
    -        protocol: TCP	
    +        protocol: TCP
    +      - containerPort: 32189
    +        hostPort: 2182
    +        protocol: TCP
    +      - containerPort: 32185
    +        hostPort: 2175
    +        protocol: TCP
    +      - containerPort: 32143
    +        hostPort: 2143
    +        protocol: TCP
    +      - containerPort: 32343
    +        hostPort: 2343
    +        protocol: TCP
         extraMounts:
           - hostPath: ${pv_root}
             containerPath: ${pv_root}
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java
    index 23234581d0e..512f79b2bb9 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java
    @@ -1,9 +1,11 @@
    -// Copyright (c) 2021, 2023, Oracle and/or its affiliates.
    +// Copyright (c) 2021, 2024, Oracle and/or its affiliates.
     // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     
     package oracle.weblogic.kubernetes;
     
     import java.io.IOException;
    +import java.net.InetAddress;
    +import java.net.UnknownHostException;
     import java.nio.charset.StandardCharsets;
     import java.nio.file.Files;
     import java.nio.file.Path;
    @@ -37,13 +39,21 @@
     import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE;
     import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT;
     import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST;
    +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER;
     import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI;
     import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE;
     import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME;
     import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG;
    +import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTPS_NODEPORT;
    +import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTP_HOSTPORT;
    +import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTP_NODEPORT;
     import static oracle.weblogic.kubernetes.TestConstants.OKD;
     import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT;
     import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP;
    +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT;
    +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT;
    +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER;
    +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT;
     import static oracle.weblogic.kubernetes.actions.ActionConstants.REMOTECONSOLE_DOWNLOAD_URL;
     import static oracle.weblogic.kubernetes.actions.ActionConstants.REMOTECONSOLE_FILE;
     import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR;
    @@ -54,6 +64,7 @@
     import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReady;
     import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReturnedCode;
     import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createSSLenabledMiiDomainAndVerify;
    +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke;
     import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createIngressAndRetryIfFail;
    @@ -92,6 +103,7 @@ class ItRemoteConsole {
       private static HelmParams traefikHelmParams = null;
       private static NginxParams nginxHelmParams = null;
       private static int nginxNodePort;
    +  private static String nginxHostAndPort;
     
       // domain constants
       private static final String domainUid = "domain1";
    @@ -100,6 +112,7 @@ class ItRemoteConsole {
       private static final String managedServerPrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE;
       private static LoggingFacade logger = null;
       private static final int ADMIN_SERVER_PORT = 7001;
    +  private static final int adminServerSecurePort = 7008;
       private static String adminSvcExtHost = null;
     
       /**
    @@ -109,7 +122,7 @@ class ItRemoteConsole {
        *                   JUnit engine parameter resolution mechanism
        */
       @BeforeAll
    -  public static void initAll(@Namespaces(4) List namespaces) {
    +  public static void initAll(@Namespaces(4) List namespaces) throws Exception {
         logger = getLogger();
         // get a unique operator namespace
         logger.info("Getting a unique namespace for operator");
    @@ -137,15 +150,6 @@ public static void initAll(@Namespaces(4) List namespaces) {
         // install and verify operator
         installAndVerifyOperator(opNamespace, domainNamespace);
     
    -    if (!OKD) {
    -      logger.info("Installing Traefik controller using helm");
    -      traefikHelmParams = installAndVerifyTraefik(traefikNamespace, 0, 0).getHelmParams();
    -
    -
    -      // install and verify Nginx
    -      nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0);
    -    }
    -
         createSSLenabledMiiDomainAndVerify(
             domainNamespace,
             domainUid,
    @@ -154,18 +158,20 @@ public static void initAll(@Namespaces(4) List namespaces) {
             managedServerPrefix,
             replicaCount);
     
    -    // create ingress rules with path routing for Traefik and NGINX
         if (!OKD) {
    -      createTraefikIngressRoutingRules(domainNamespace);
    -      createNginxIngressPathRoutingRules();
    +
    +      logger.info("Installing Traefik controller using helm");
    +      installTraefikIngressController();
    +
    +      // install and verify Nginx
    +      logger.info("Installing Ngnix controller using helm");
    +      installNgnixIngressController();
    +
         }
     
         // install WebLogic remote console
         assertTrue(installAndVerifyWlsRemoteConsole(domainNamespace, adminServerPodName),
             "Remote Console installation failed");
    -
    -    // Verify k8s WebLogic domain is accessible through remote console using admin server nodeport
    -    verifyWlsRemoteConsoleConnection();
       }
     
       /**
    @@ -176,12 +182,21 @@ public static void initAll(@Namespaces(4) List namespaces) {
       @DisabledIfEnvironmentVariable(named = "OKD", matches = "true")
       void testWlsRemoteConsoleConnectionThroughTraefik() {
     
    -    int traefikNodePort = getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), "web");
    +    int traefikNodePort = -1;
    +    if (traefikHelmParams != null) {
    +      logger.info("Getting web node port for Traefik loadbalancer {0}", traefikHelmParams.getReleaseName());
    +      traefikNodePort = getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), "web");
    +    } else if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      logger.info("Getting node port for Traefik loadbalancer from init installation");
    +      traefikNodePort = TRAEFIK_INGRESS_HTTP_HOSTPORT;
    +      logger.info("Got node port {0} for Traefik loadbalancer", TRAEFIK_INGRESS_HTTP_HOSTPORT);
    +    }
    +
         assertNotEquals(-1, traefikNodePort,
             "Could not get the default external service node port");
         logger.info("Found the Traefik service nodePort {0}", traefikNodePort);
         logger.info("The K8S_NODEPORT_HOST is {0}", K8S_NODEPORT_HOST);
    -    verifyRemoteConsoleConnectionThroughLB(traefikNodePort);
    +    verifyRemoteConsoleConnectionThroughLB(traefikNodePort, "traefik");
         logger.info("WebLogic domain is accessible through remote console using Traefik");
       }
     
    @@ -197,7 +212,7 @@ void testWlsRemoteConsoleConnectionThroughNginx() {
         logger.info("Found the NGINX service nodePort {0}", nginxNodePort);
         logger.info("The K8S_NODEPORT_HOST is {0}", K8S_NODEPORT_HOST);
     
    -    verifyRemoteConsoleConnectionThroughLB(nginxNodePort);
    +    verifyRemoteConsoleConnectionThroughLB(nginxNodePort, "nginx");
         logger.info("WebLogic domain is accessible through remote console using NGINX");
       }
     
    @@ -222,11 +237,21 @@ void testWlsRemoteConsoleConnectionUsingSSL() {
              domainNamespace, getExternalServicePodName(adminServerPodName), "default-secure");
         setTargetPortForRoute("domain1-admin-server-sslport-ext", domainNamespace, sslPort);
         String hostAndPort = null;
    +    String httpsHostHeader = "";
    +    String header = "";
         if (!OKD) {
    -      String ingressServiceName = traefikHelmParams.getReleaseName();
    -      hostAndPort = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null
    -          ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace)
    -          : getHostAndPort(adminSvcSslPortExtHost, sslNodePort);
    +      if (KIND_CLUSTER
    +          && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +        httpsHostHeader = createIngressHostRouting(domainNamespace, domainUid,
    +          "admin-server", adminServerSecurePort);
    +        hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTPS_HOSTPORT;
    +        header = " -H 'host: " + httpsHostHeader + "' ";
    +      } else {
    +        String ingressServiceName = traefikHelmParams.getReleaseName();
    +        hostAndPort = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null
    +            ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace)
    +            : getHostAndPort(adminSvcSslPortExtHost, sslNodePort);
    +      }
         } else {
           hostAndPort = getHostAndPort(adminSvcSslPortExtHost, sslNodePort);
         }
    @@ -234,27 +259,43 @@ void testWlsRemoteConsoleConnectionUsingSSL() {
         logger.info("The hostAndPort is {0}", hostAndPort);
     
         //verify ready app is accessible through default-secure nodeport
    -    String curlCmd = "curl -g -sk --show-error --noproxy '*' "
    +    String curlCmd = "";
    +    if (KIND_CLUSTER
    +          && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      curlCmd = "curl -g -sk --show-error --noproxy '*' "
    +          + header + " https://" + hostAndPort
    +          + "/weblogic/ready --write-out %{http_code} -o /dev/null";
    +    } else {
    +      curlCmd = "curl -g -sk --show-error --noproxy '*' "
               + " https://" + hostAndPort
               + "/weblogic/ready --write-out %{http_code} -o /dev/null";
    +    }
         logger.info("Executing WebLogic console default-secure nodeport curl command {0}", curlCmd);
         assertTrue(callWebAppAndWaitTillReady(curlCmd, 10));
         logger.info("ready app is accessible thru default-secure service");
     
    -    //verify remote console is accessible through default-secure nodeport
    -    //The final complete curl command to run is like:
    -    //curl -sk -v --show-error --user username:password http://localhost:8012/api/providers/AdminServerConnection -H
    -    //"Content-Type:application/json" --data "{ \"name\": \"asconn\", \"domainUrl\": \"https://myhost://nodeport\"}"
    -    //--write-out %{http_code} -o /dev/null
    -    curlCmd = "curl -g -sk -v --show-error --noproxy '*' --user "
    -        + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT
    -        + " http://localhost:8012/api/providers/AdminServerConnection -H  "
    -        + "\"" + "Content-Type:application/json" + "\""
    -        + " --data "
    -        + "\"{\\" + "\"name\\" + "\"" + ": " + "\\" + "\"" + "asconn\\" + "\"" + ", "
    -        + "\\" + "\"domainUrl\\" + "\"" + ": " + "\\" + "\"" + "https://"
    -        + hostAndPort + "\\" + "\"}" + "\""
    -        + " --write-out %{http_code} -o /dev/null";
    +    if (KIND_CLUSTER
    +          && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      curlCmd = "curl -g -sk -v --show-error --noproxy '*' --user "
    +          + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT
    +          + " http://localhost:8012/api/providers/AdminServerConnection -H  "
    +          + "\"" + "Content-Type:application/json" + "\""
    +          + " --data "
    +          + "\"{\\" + "\"name\\" + "\"" + ": " + "\\" + "\"" + "asconn\\" + "\"" + ", "
    +          + "\\" + "\"domainUrl\\" + "\"" + ": " + "\\" + "\"" + header
    +          + "https://" + hostAndPort + "\\" + "\"}" + "\""
    +          + " --write-out %{http_code} -o /dev/null";
    +    } else {
    +      curlCmd = "curl -g -sk -v --show-error --noproxy '*' --user "
    +          + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT
    +          + " http://localhost:8012/api/providers/AdminServerConnection -H  "
    +          + "\"" + "Content-Type:application/json" + "\""
    +          + " --data "
    +          + "\"{\\" + "\"name\\" + "\"" + ": " + "\\" + "\"" + "asconn\\" + "\"" + ", "
    +          + "\\" + "\"domainUrl\\" + "\"" + ": " + "\\" + "\"" + "https://"
    +          + hostAndPort + "\\" + "\"}" + "\""
    +          + " --write-out %{http_code} -o /dev/null";
    +    }
         logger.info("Executing remote console default-secure nodeport curl command {0}", curlCmd);
         assertTrue(callWebAppAndWaitTillReturnedCode(curlCmd, "201", 10), "Calling web app failed");
         logger.info("Remote console is accessible through default-secure service");
    @@ -295,7 +336,7 @@ private static void createTraefikIngressRoutingRules(String domainNamespace) {
         }
       }
     
    -  private static void createNginxIngressPathRoutingRules() {
    +  private static void createNginxIngressPathRoutingRules() throws UnknownHostException {
     
         // create an ingress in domain namespace
         String ingressName = domainNamespace + "-nginx-path-routing";
    @@ -335,63 +376,72 @@ private static void createNginxIngressPathRoutingRules() {
         logger.info("ingress {0} was created in namespace {1}", ingressName, domainNamespace);
     
         // check the ingress is ready to route the app to the server pod
    +
         String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller";
    -    nginxNodePort = assertDoesNotThrow(() -> getServiceNodePort(nginxNamespace, nginxServiceName, "http"),
    +    logger.info("nginxServiceName is {0}", nginxServiceName);
    +
    +    if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      nginxNodePort = NGINX_INGRESS_HTTP_HOSTPORT;
    +    } else if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      nginxNodePort = assertDoesNotThrow(() -> getServiceNodePort(nginxNamespace, nginxServiceName, "http"),
             "Getting Nginx loadbalancer service node port failed");
    +    }
    +    logger.info("nginxNodePort is {0}", nginxNodePort);
    +
         String host = K8S_NODEPORT_HOST;
         if (host.contains(":")) {
           host = "[" + host + "]";
         }
    -    String hostAndPort = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null
    +    if (KIND_CLUSTER
    +        && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      host = InetAddress.getLocalHost().getHostAddress();
    +    }
    +
    +    nginxHostAndPort = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null
             ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : host + ":" + nginxNodePort;
    +    logger.info("nginxHostAndPort is {0}", nginxHostAndPort);
     
    -    String curlCmd = "curl -g --silent --show-error --noproxy '*' http://" + hostAndPort
    +    String curlCmd = "curl -g --silent --show-error --noproxy '*' http://" + nginxHostAndPort
             + "/weblogic/ready --write-out %{http_code} -o /dev/null";
     
         logger.info("Executing curl command {0}", curlCmd);
         assertTrue(callWebAppAndWaitTillReady(curlCmd, 60));
       }
     
    -  private static void verifyWlsRemoteConsoleConnection() {
    -    int nodePort = getServiceNodePort(
    -        domainNamespace, getExternalServicePodName(adminServerPodName), "default");
    -    assertNotEquals(-1, nodePort,
    -        "Could not get the default external service node port");
    -    logger.info("Found the default service nodePort {0}", nodePort);
    -    logger.info("The K8S_NODEPORT_HOST is {0}", K8S_NODEPORT_HOST);
    +  private static void installTraefikIngressController() {
     
    -    if (adminSvcExtHost == null) {
    -      adminSvcExtHost = createRouteForOKD(getExternalServicePodName(adminServerPodName), domainNamespace);
    +    if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      logger.info("Installing Traefik controller using helm");
    +      traefikHelmParams = installAndVerifyTraefik(traefikNamespace, 0, 0).getHelmParams();
         }
    -    logger.info("admin svc host = {0}", adminSvcExtHost);
    -    String hostAndPort = getHostAndPort(adminSvcExtHost, nodePort);
     
    -    String curlCmd = "curl -g -v --show-error --noproxy '*' --user "
    -        + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT
    -        + " http://localhost:8012/api/providers/AdminServerConnection -H "
    -        + "\"" + "Content-Type:application/json" + "\""
    -        + " --data "
    -        + "\"{\\" + "\"name\\" + "\"" + ": " + "\\" + "\"" + "asconn\\" + "\"" + ", "
    -        + "\\" + "\"domainUrl\\" + "\"" + ": " + "\\" + "\"" + "http://"
    -        + hostAndPort + "\\" + "\"}" + "\""
    -        + " --write-out %{http_code} -o /dev/null";
    -    logger.info("Executing default nodeport curl command {0}", curlCmd);
    -    assertTrue(callWebAppAndWaitTillReturnedCode(curlCmd, "201", 10), "Calling web app failed");
    -    logger.info("WebLogic domain is accessible through remote console");
    +    createTraefikIngressRoutingRules(domainNamespace);
    +
    +  }
    +
    +  private static void installNgnixIngressController() throws UnknownHostException {
    +
    +    if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      logger.info("Installing Ngnix controller using 0 as nodeport");
    +      nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0);
    +    } else if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) {
    +      logger.info("Installing Ngnix controller using http_nodeport {0}, https_nodeport {1}",
    +          NGINX_INGRESS_HTTP_NODEPORT, NGINX_INGRESS_HTTPS_NODEPORT);
    +      nginxHelmParams = installAndVerifyNginx(nginxNamespace,
    +          NGINX_INGRESS_HTTP_NODEPORT, NGINX_INGRESS_HTTPS_NODEPORT);
    +    }
    +
    +    createNginxIngressPathRoutingRules();
    +
       }
     
    -  private static void verifyRemoteConsoleConnectionThroughLB(int nodePortOfLB) {
    +  private static void verifyRemoteConsoleConnectionThroughLB(int nodePortOfLB, String type) {
         logger.info("LB nodePort is {0}", nodePortOfLB);
         logger.info("The K8S_NODEPORT_HOST is {0}", K8S_NODEPORT_HOST);
     
    -    String ingressServiceName = traefikHelmParams.getReleaseName();
    -    String traefikNamespace = traefikHelmParams.getNamespace();
    -    String host = K8S_NODEPORT_HOST;
    -    if (host.contains(":")) {
    -      host = "[" + host + "]";
    -    }
    -    String hostAndPort = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null
    -        ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) : host + ":" + nodePortOfLB;
    +    String hostAndPort = getLBhostAndPort(nodePortOfLB, type);
    +
    +    logger.info("For loadbalancer {0} hostAndPort is {0}", type, hostAndPort);
     
         String curlCmd = "curl -g -v --user " + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT
             + " http://localhost:8012/api/providers/AdminServerConnection -H "
    @@ -406,6 +456,31 @@ private static void verifyRemoteConsoleConnectionThroughLB(int nodePortOfLB) {
             "Calling web app failed");
       }
     
    +  private static String getLBhostAndPort(int nodePortOfLB, String type) {
    +    String host = K8S_NODEPORT_HOST;
    +    String hostAndPort = null;
    +    if (host.contains(":")) {
    +      host = "[" + host + "]";
    +    }
    +    String ingressServiceName = null;
    +    String traefikNamespace = null;
    +    if (type.equalsIgnoreCase("traefik")) {
    +      if (traefikHelmParams != null) {
    +        ingressServiceName = traefikHelmParams.getReleaseName();
    +        traefikNamespace = traefikHelmParams.getNamespace();
    +        hostAndPort = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null
    +            ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) : host + ":" + nodePortOfLB;
    +      } else {
    +        logger.info("traefikHelmParams is null");
    +        hostAndPort = host + ":" + nodePortOfLB;
    +      }
    +    } else if (type.equalsIgnoreCase("nginx")) {
    +      hostAndPort = nginxHostAndPort;
    +    }
    +
    +    return hostAndPort;
    +  }
    +
       /**
        * Install WebLogic Remote Console.
        * @param domainNamespace namespace in which the domain will be created
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java
    index 87f82050e02..243133eb50c 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java
    @@ -205,10 +205,10 @@ public interface TestConstants {
       public static final String TEST_NGINX_IMAGE_NAME = TEST_IMAGES_TENANCY + "/test-images/ingress-nginx/controller";
       public static final String NGINX_INGRESS_IMAGE_TAG = "v1.2.0";
       public static final String NGINX_NAMESPACE = "ns-nginx";
    -  public static final int NGINX_INGRESS_HTTP_NODEPORT = 30880;
    -  public static final int NGINX_INGRESS_HTTPS_NODEPORT = 30443;
    -  public static final int NGINX_INGRESS_HTTP_HOSTPORT = 2080;
    -  public static final int NGINX_INGRESS_HTTPS_HOSTPORT = 2443;
    +  public static final int NGINX_INGRESS_HTTP_NODEPORT = 31880;
    +  public static final int NGINX_INGRESS_HTTPS_NODEPORT = 31443;
    +  public static final int NGINX_INGRESS_HTTP_HOSTPORT = 2180;
    +  public static final int NGINX_INGRESS_HTTPS_HOSTPORT = 2543;
       
       public static final Path INGRESS_CLASS_FILE_NAME = assertDoesNotThrow(()
           -> Files.createTempFile("ingressclass", ".name"));  
    
    From 0698b39834cb1f0a4ebda5055400782a5a4889d8 Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Fri, 5 Apr 2024 17:03:17 +0000
    Subject: [PATCH 023/356] Merge branch 'fix-unzip-exploded-ear' into 'main'
    
    fix unzipping exploded format omitted the dd due to regex.
    
    See merge request weblogic-cloud/weblogic-kubernetes-operator!4649
    
    (cherry picked from commit b0c1690a745c86a95ff5e25100c67bb5df26df5f)
    
    b80a4fe6 fix unzipping exploded format omitted the dd due to regex.
    3dedd584 remove unnecessary exclude.
    ---
     operator/src/main/resources/scripts/introspectDomain.py | 2 +-
     operator/src/main/resources/scripts/modelInImage.sh     | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/operator/src/main/resources/scripts/introspectDomain.py b/operator/src/main/resources/scripts/introspectDomain.py
    index c87fb64b267..c37292783d1 100644
    --- a/operator/src/main/resources/scripts/introspectDomain.py
    +++ b/operator/src/main/resources/scripts/introspectDomain.py
    @@ -1131,7 +1131,7 @@ def addDomainConfig(self):
                   "--exclude=$DOMAIN_HOME/config/wlsdeploy/custom " \
                   "--exclude=$DOMAIN_HOME/config/deployments " \
                   "--exclude=$DOMAIN_HOME/config/fmwconfig " \
    -              "$DOMAIN_HOME/wlsdeploy/applications/*.xml " \
    +              "$DOMAIN_HOME/wlsdeploy/applications/./*.xml " \
                   "$DOMAIN_HOME/config " \
                   "$DOMAIN_HOME/security/saml*.properties " \
                   "$DOMAIN_HOME/security/*.xml " % tar_name
    diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh
    index 7483fb6778c..2d3123d9f8d 100755
    --- a/operator/src/main/resources/scripts/modelInImage.sh
    +++ b/operator/src/main/resources/scripts/modelInImage.sh
    @@ -1330,7 +1330,7 @@ restoreAppAndLibs() {
             #   wlsdeploy/applications/*.xml since it is included int zipped up domain config
             #   zip, the original xml in the archive may have wdt tokenized notations.
             cd ${DOMAIN_HOME} || return 1
    -        unzip -o ${IMG_ARCHIVES_ROOTDIR}/${file} -x "wlsdeploy/domainBin/*" -x "wlsdeploy/domainLibraries/*" -x "wlsdeploy/applications/*.xml" -x "config/*"
    +        unzip -o ${IMG_ARCHIVES_ROOTDIR}/${file} -x "wlsdeploy/domainBin/*" "wlsdeploy/domainLibraries/*"  "config/*"
             if [ $? -ne 0 ] ; then
               trace SEVERE "Domain Source Type is FromModel, error in extracting application archive ${IMG_ARCHIVES_ROOTDIR}/${file}"
               return 1
    
    From f3d530debcf70285dd302aa2a120f274387cf5e0 Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 8 Apr 2024 08:59:02 -0400
    Subject: [PATCH 024/356] Dependency updates
    
    ---
     pom.xml | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/pom.xml b/pom.xml
    index 67fb92f550e..46c7e4469a1 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -699,14 +699,14 @@
         1.9.23
         4.12.0
         3.9.0
    -    1.77
    +    1.78
         5.10.2
         5.7.1
         1.7.0
         1.3.2
         UTF-8
    -    3.1.5
    -    1.1.5
    +    3.1.6
    +    1.1.6
         4.0.2
         6.0.0
         0.16.0
    
    From e6ac418fd52c4361204921da028af70d1fe5c1fd Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 8 Apr 2024 17:23:44 -0400
    Subject: [PATCH 025/356] Depencency updates
    
    ---
     pom.xml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/pom.xml b/pom.xml
    index 46c7e4469a1..ac53127db0a 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -692,7 +692,7 @@
         3.5.0
         1.0.0
         3.25.3
    -    2.16.0
    +    2.16.1
         4.2.1
         19.0.1
         3.0.1u2
    
    From a424657b3cc135a1ed2dc024e6dd4fbb3fc6377a Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Tue, 9 Apr 2024 09:44:36 -0400
    Subject: [PATCH 026/356] Dependency updates
    
    ---
     pom.xml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/pom.xml b/pom.xml
    index ac53127db0a..66350abf4d5 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -716,7 +716,7 @@
         2.10.1
         9.1.0
         2.0.12
    -    1.5.3
    +    1.5.4
         ${project.basedir}/src-generated-swagger
         ${root-generated-swagger}/main/java
         ${project.basedir}/swagger/domain.json
    
    From e90d3dbcb426a5669eb9ecdd51492156a2432113 Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Tue, 9 Apr 2024 11:37:01 -0400
    Subject: [PATCH 027/356] Prepare for WKO 4.2.1
    
    ---
     common/pom.xml                      | 2 +-
     domain-upgrader/pom.xml             | 2 +-
     integration-tests/pom.xml           | 2 +-
     json-schema-generator/pom.xml       | 2 +-
     kubernetes/pom.xml                  | 2 +-
     operator-build-maven-plugin/pom.xml | 2 +-
     operator/pom.xml                    | 2 +-
     pom.xml                             | 2 +-
     swagger-generator/pom.xml           | 2 +-
     9 files changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/common/pom.xml b/common/pom.xml
    index 41ea968a07e..4f86f9ecc88 100644
    --- a/common/pom.xml
    +++ b/common/pom.xml
    @@ -6,7 +6,7 @@
       
         oracle.kubernetes
         operator-parent
    -    4.2.1-SNAPSHOT
    +    4.2.1
       
     
       common
    diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml
    index 97296072f26..40b7d15e5d8 100644
    --- a/domain-upgrader/pom.xml
    +++ b/domain-upgrader/pom.xml
    @@ -7,7 +7,7 @@
       
         oracle.kubernetes
         operator-parent
    -    4.2.1-SNAPSHOT
    +    4.2.1
       
     
       domain-upgrader
    diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
    index aaf3f48aa1c..353471d43dd 100644
    --- a/integration-tests/pom.xml
    +++ b/integration-tests/pom.xml
    @@ -7,7 +7,7 @@
         
             oracle.kubernetes
             operator-parent
    -        4.2.1-SNAPSHOT
    +        4.2.1
         
     
         integration-tests
    diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml
    index f7f22d0e3dd..55241b44029 100644
    --- a/json-schema-generator/pom.xml
    +++ b/json-schema-generator/pom.xml
    @@ -7,7 +7,7 @@
         
             operator-parent
             oracle.kubernetes
    -        4.2.1-SNAPSHOT
    +        4.2.1
         
     
         json-schema
    diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml
    index c230262515a..d8e2cc78614 100644
    --- a/kubernetes/pom.xml
    +++ b/kubernetes/pom.xml
    @@ -9,7 +9,7 @@
         
             oracle.kubernetes
             operator-parent
    -        4.2.1-SNAPSHOT
    +        4.2.1
         
     
         installation-tests
    diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml
    index af726b2e09a..2bec9080af2 100644
    --- a/operator-build-maven-plugin/pom.xml
    +++ b/operator-build-maven-plugin/pom.xml
    @@ -7,7 +7,7 @@
         
             operator-parent
             oracle.kubernetes
    -        4.2.1-SNAPSHOT
    +        4.2.1
         
     
         operator-build-maven-plugin
    diff --git a/operator/pom.xml b/operator/pom.xml
    index 13c3a115d71..59510153488 100644
    --- a/operator/pom.xml
    +++ b/operator/pom.xml
    @@ -7,7 +7,7 @@
       
         oracle.kubernetes
         operator-parent
    -    4.2.1-SNAPSHOT
    +    4.2.1
       
     
       weblogic-kubernetes-operator
    diff --git a/pom.xml b/pom.xml
    index 66350abf4d5..9f85c3fde51 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -7,7 +7,7 @@
     
       oracle.kubernetes
       operator-parent
    -  4.2.1-SNAPSHOT
    +  4.2.1
     
       
         operator
    diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml
    index 750dcd2b588..65004a3841e 100644
    --- a/swagger-generator/pom.xml
    +++ b/swagger-generator/pom.xml
    @@ -7,7 +7,7 @@
         
             oracle.kubernetes
             operator-parent
    -        4.2.1-SNAPSHOT
    +        4.2.1
         
     
         operator-swagger
    
    From 013cc79550e0d8af5c674f23d9ef263502d79e22 Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Tue, 9 Apr 2024 14:06:55 -0400
    Subject: [PATCH 028/356] Prepare for next development iteration
    
    ---
     common/pom.xml                      | 2 +-
     domain-upgrader/pom.xml             | 2 +-
     integration-tests/pom.xml           | 2 +-
     json-schema-generator/pom.xml       | 2 +-
     kubernetes/pom.xml                  | 2 +-
     operator-build-maven-plugin/pom.xml | 2 +-
     operator/pom.xml                    | 2 +-
     pom.xml                             | 2 +-
     swagger-generator/pom.xml           | 2 +-
     9 files changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/common/pom.xml b/common/pom.xml
    index 4f86f9ecc88..1e43524323a 100644
    --- a/common/pom.xml
    +++ b/common/pom.xml
    @@ -6,7 +6,7 @@
       
         oracle.kubernetes
         operator-parent
    -    4.2.1
    +    4.2.2-SNAPSHOT
       
     
       common
    diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml
    index 40b7d15e5d8..c190e1a6d29 100644
    --- a/domain-upgrader/pom.xml
    +++ b/domain-upgrader/pom.xml
    @@ -7,7 +7,7 @@
       
         oracle.kubernetes
         operator-parent
    -    4.2.1
    +    4.2.2-SNAPSHOT
       
     
       domain-upgrader
    diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
    index 353471d43dd..17e3c8f7e32 100644
    --- a/integration-tests/pom.xml
    +++ b/integration-tests/pom.xml
    @@ -7,7 +7,7 @@
         
             oracle.kubernetes
             operator-parent
    -        4.2.1
    +        4.2.2-SNAPSHOT
         
     
         integration-tests
    diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml
    index 55241b44029..89015776c30 100644
    --- a/json-schema-generator/pom.xml
    +++ b/json-schema-generator/pom.xml
    @@ -7,7 +7,7 @@
         
             operator-parent
             oracle.kubernetes
    -        4.2.1
    +        4.2.2-SNAPSHOT
         
     
         json-schema
    diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml
    index d8e2cc78614..97fd9ef73fb 100644
    --- a/kubernetes/pom.xml
    +++ b/kubernetes/pom.xml
    @@ -9,7 +9,7 @@
         
             oracle.kubernetes
             operator-parent
    -        4.2.1
    +        4.2.2-SNAPSHOT
         
     
         installation-tests
    diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml
    index 2bec9080af2..02148dfbdc5 100644
    --- a/operator-build-maven-plugin/pom.xml
    +++ b/operator-build-maven-plugin/pom.xml
    @@ -7,7 +7,7 @@
         
             operator-parent
             oracle.kubernetes
    -        4.2.1
    +        4.2.2-SNAPSHOT
         
     
         operator-build-maven-plugin
    diff --git a/operator/pom.xml b/operator/pom.xml
    index 59510153488..1ba047cc940 100644
    --- a/operator/pom.xml
    +++ b/operator/pom.xml
    @@ -7,7 +7,7 @@
       
         oracle.kubernetes
         operator-parent
    -    4.2.1
    +    4.2.2-SNAPSHOT
       
     
       weblogic-kubernetes-operator
    diff --git a/pom.xml b/pom.xml
    index 9f85c3fde51..06a51d208a3 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -7,7 +7,7 @@
     
       oracle.kubernetes
       operator-parent
    -  4.2.1
    +  4.2.2-SNAPSHOT
     
       
         operator
    diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml
    index 65004a3841e..5f021ffdfb1 100644
    --- a/swagger-generator/pom.xml
    +++ b/swagger-generator/pom.xml
    @@ -7,7 +7,7 @@
         
             oracle.kubernetes
             operator-parent
    -        4.2.1
    +        4.2.2-SNAPSHOT
         
     
         operator-swagger
    
    From 6f76e42413892b2a542be5178fd6f672ed50de8b Mon Sep 17 00:00:00 2001
    From: "ANTARYAMI.PANIGRAHI" 
    Date: Wed, 10 Apr 2024 00:53:33 +0000
    Subject: [PATCH 029/356] Backport (releasse/4.2) removal of internal DNS
     address
    
    ---
     .../kubernetes/ItMiiDomainUpgradeToSecureMode.java        | 5 +++--
     .../oracle/weblogic/kubernetes/utils/CommonTestUtils.java | 4 ++--
     .../java/oracle/weblogic/kubernetes/utils/IstioUtils.java | 8 ++++++--
     3 files changed, 11 insertions(+), 6 deletions(-)
    
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java
    index 2630ffe14bf..8110427e4a4 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java
    @@ -1,4 +1,4 @@
    -// Copyright (c) 2023, Oracle and/or its affiliates.
    +// Copyright (c) 2023, 2024, Oracle and/or its affiliates.
     // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     
     package oracle.weblogic.kubernetes;
    @@ -123,7 +123,8 @@ class ItMiiDomainUpgradeToSecureMode {
       String clusterIngressHost;
       private final String imageTag1411 = "14.1.1.0-11";
       private final String imageTag12214 = "12.2.1.4";
    -  private final String image1412 = "wls-docker-dev-local.dockerhub-phx.oci.oraclecorp.com/weblogic:14.1.2.0.0";
    +  private final String imageTag1412 = "14.1.2.0.0-jdk17";
    +  private final String image1412 = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag1412;
       private final String sampleAppUri = "/sample-war/index.jsp";
       private final String adminAppUri = "/management/tenant-monitoring/servers";
       private final String adminAppText = "RUNNING";
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java
    index a530cc5a0d7..632759e02a5 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java
    @@ -2047,7 +2047,7 @@ public static String getKindRepoValue(String propertyName) {
     
       /**
        * Given a repo and tenancy name, determine the prefix length.  For example,
    -   * phx.ocir.io/foobar/test-images/myimage will treat phx.ocir.io/foobar/ as
    +   * cloud.io/foobar/test-images/myimage will treat cloud.io/foobar/ as
        * the prefix so the length is 19.
        *
        * @param baseRepo    base repo name
    @@ -2465,4 +2465,4 @@ public static void createIngressPathRouting(String namespace, String path,
         getLogger().info("ingress {0} was created in namespace {1}", ingressName, namespace);
       }
       
    -}
    \ No newline at end of file
    +}
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java
    index 0385dfe4a79..35268019473 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java
    @@ -28,6 +28,8 @@
     import oracle.weblogic.kubernetes.utils.SemanticVersion.Compatibility;
     
     import static oracle.weblogic.kubernetes.TestConstants.ARM;
    +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO;
    +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_TENANCY;
     import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION;
     import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS;
     import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES;
    @@ -90,10 +92,12 @@ public static void installIstio() {
             Paths.get(RESULTS_ROOT, "install-istio.sh");
         String installScript = istioInstallPath.toString();
     
    -    // When install istio in OCNE environment, use phx.ocir.io/devweblogic/istio-release instead of gcr.io/istio-release
    +    // When install istio in OCNE environment, 
    +    // use BASE_IMAGES_REPO/devweblogic/istio-release instead of gcr.io/istio-release
         if (OCNE) {
    +      String ocneIstioRepo = BASE_IMAGES_REPO + "/" + BASE_IMAGES_TENANCY; 
           logger.info("replace istio installation hub in File {0}", installScript);
    -      assertDoesNotThrow(() -> replaceStringInFile(installScript, "gcr.io", "phx.ocir.io/devweblogic"),
    +      assertDoesNotThrow(() -> replaceStringInFile(installScript, "gcr.io", ocneIstioRepo),
               String.format("Failed to replace string in File %s", installScript));
         }
         String arch = "linux-amd64";
    
    From d5751c2882120d90ad3fa832e0d8d07ef7053ab0 Mon Sep 17 00:00:00 2001
    From: vanajakshi_mukkara 
    Date: Wed, 10 Apr 2024 16:45:14 +0000
    Subject: [PATCH 030/356] backporting podman conversion changes in ItMiiDomain
     and ItMiiDynamicUpdatePart3
    
    ---
     .../weblogic/kubernetes/ItMiiDomain.java      | 60 ++++++++------
     .../kubernetes/ItMiiDynamicUpdatePart3.java   | 80 ++++++++++++++++---
     .../kubernetes/utils/CommonMiiTestUtils.java  |  4 +-
     .../kubernetes/utils/CommonTestUtils.java     |  2 +-
     4 files changed, 109 insertions(+), 37 deletions(-)
    
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java
    index 5ff6bf874c0..c90828f5b3e 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java
    @@ -67,7 +67,8 @@
     import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_PASSWORD;
     import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME;
     import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_USERNAME;
    -import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_SLIM;
    +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT;
    +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT;
     import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER;
     import static oracle.weblogic.kubernetes.TestConstants.WLS_DOMAIN_TYPE;
     import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR;
    @@ -100,13 +101,13 @@
     import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkClusterReplicaCountMatches;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists;
    +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.isAppInServerPodReady;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.startPortForwardProcess;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.stopPortForwardProcess;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil;
    -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyCredentials;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withQuickRetryPolicy;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy;
    @@ -204,6 +205,8 @@ void testCreateMiiDomain() {
         final String hostName = "localhost";
         final int adminServerPort = 7001;
         final int adminServerSecurePort = 7008;
    +    String httpHostHeader = "";
    +    String httpsHostHeader = "";
     
         // Create the repo secret to pull the image
         // this secret is used only for non-kind cluster
    @@ -287,6 +290,18 @@ void testCreateMiiDomain() {
               "Could not get the default-secure external service node port");
         logger.info("Found the administration service nodePort {0}", sslNodePort);
         String hostAndPort = getHostAndPort(adminSvcSslPortExtHost, sslNodePort);
    +    // create ingress for admin service
    +    // use traefik LB for kind cluster with ingress host header in url
    +    String headers = "";
    +    if (TestConstants.KIND_CLUSTER
    +        && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) {
    +      httpHostHeader = createIngressHostRouting(domainNamespace, domainUid,
    +          "admin-server", adminServerPort);
    +      httpsHostHeader = createIngressHostRouting(domainNamespace, domainUid,
    +          "admin-server", adminServerSecurePort);
    +      hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTPS_HOSTPORT;
    +      headers = " -H 'host: " + httpsHostHeader + "' ";
    +    }
     
         final String resourcePath = "/weblogic/ready";
         if (OKE_CLUSTER) {
    @@ -298,7 +313,7 @@ void testCreateMiiDomain() {
               adminServerPodName);
         } else {
           String curlCmd = "curl -skg --show-error --noproxy '*' "
    -          + " https://" + hostAndPort
    +          + headers + " https://" + hostAndPort
               + "/weblogic/ready --write-out %{http_code} -o /dev/null";
           logger.info("Executing default-admin nodeport curl command {0}", curlCmd);
           assertTrue(callWebAppAndWaitTillReady(curlCmd, 10));
    @@ -311,28 +326,27 @@ void testCreateMiiDomain() {
             "Could not get the default external service node port");
         logger.info("Found the default service nodePort {0}", nodePort);
         hostAndPort = getHostAndPort(adminSvcExtHost, nodePort);
    -
    -    if (!WEBLOGIC_SLIM) {
    -      if (OKE_CLUSTER) {
    -        testUntil(
    -            isAppInServerPodReady(domainNamespace,
    -                adminServerPodName, 7001, resourcePath, ""),
    -            logger, "verify EM console access {0} in server {1}",
    -            resourcePath,
    -            adminServerPodName);
    -      } else {
    -        String curlCmd2 = "curl -skg --show-error --noproxy '*' "
    -            + " http://" + hostAndPort
    -            + "/weblogic/ready --write-out %{http_code} -o /dev/null";
    -        logger.info("Executing default nodeport curl command {0}", curlCmd2);
    -        assertTrue(callWebAppAndWaitTillReady(curlCmd2, 5));
    -      }
    -      logger.info("ready app is accessible thru default service");
    +    if (TestConstants.KIND_CLUSTER
    +        && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) {
    +      hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT;
    +      headers = " -H 'host: " + httpHostHeader + "' ";
    +    }
    +    if (OKE_CLUSTER) {
    +      testUntil(
    +          isAppInServerPodReady(domainNamespace,
    +              adminServerPodName, 7001, resourcePath, ""),
    +          logger, "verify EM console access {0} in server {1}",
    +          resourcePath,
    +          adminServerPodName);
         } else {
    -      logger.info("Checking Rest API management console in WebLogic slim image");
    -      verifyCredentials(7001, adminServerPodName, domainNamespace,
    -          ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, true);
    +      String curlCmd2 = "curl -skg --show-error --noproxy '*' "
    +          + headers + " http://" + hostAndPort
    +          + "/weblogic/ready --write-out %{http_code} -o /dev/null";
    +      logger.info("Executing default nodeport curl command {0}", curlCmd2);
    +      assertTrue(callWebAppAndWaitTillReady(curlCmd2, 5));
         }
    +    logger.info("ready app is accessible thru default service");
    +
     
         // Test that `kubectl port-foward` is able to forward a local port to default channel port (7001 in this test)
         // and default secure channel port (7002 in this test)
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java
    index 11b93707c7f..662f24df3fa 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java
    @@ -14,6 +14,8 @@
     import io.kubernetes.client.openapi.models.V1Pod;
     import oracle.weblogic.domain.DomainCondition;
     import oracle.weblogic.domain.DomainResource;
    +import oracle.weblogic.kubernetes.actions.impl.primitive.Command;
    +import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams;
     import oracle.weblogic.kubernetes.annotations.IntegrationTest;
     import oracle.weblogic.kubernetes.annotations.Namespaces;
     import oracle.weblogic.kubernetes.logging.LoggingFacade;
    @@ -24,8 +26,12 @@
     import org.junit.jupiter.api.Tag;
     import org.junit.jupiter.api.Test;
     
    +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT;
    +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT;
     import static oracle.weblogic.kubernetes.TestConstants.MII_DYNAMIC_UPDATE_EXPECTED_ERROR_MSG;
    +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER;
     import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME;
    +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT;
     import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_SLIM;
     import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_VERSION;
     import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR;
    @@ -45,7 +51,10 @@
     import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.verifyPodIntrospectVersionUpdated;
     import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.verifyPodsNotRolled;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkSystemResourceConfig;
    +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkSystemResourceConfigViaAdminPod;
    +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil;
    +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifySystemResourceConfiguration;
     import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy;
     import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName;
     import static oracle.weblogic.kubernetes.utils.K8sEvents.DOMAIN_FAILED;
    @@ -80,6 +89,7 @@ class ItMiiDynamicUpdatePart3 {
       public static Path pathToChangReadsYaml = null;
       static LoggingFacade logger = null;
       private static String operatorPodName = null;
    +  private static String httpHostHeader = null;
     
       /**
        * Install Operator.
    @@ -318,15 +328,49 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() {
     
         verifyPodIntrospectVersionUpdated(pods.keySet(), introspectVersion, helper.domainNamespace);
     
    +
         // check datasource configuration using REST api
    -    int adminServiceNodePort
    -        = getServiceNodePort(helper.domainNamespace, getExternalServicePodName(helper.adminServerPodName), "default");
    -    assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid");
    -    assertTrue(checkSystemResourceConfig(helper.adminSvcExtHost, adminServiceNodePort,
    -        "JDBCSystemResources/TestDataSource2/JDBCResource/JDBCDataSourceParams",
    -        "jdbc\\/TestDataSource2-2"), "JDBCSystemResource JNDIName not found");
    +    if (OKE_CLUSTER) {
    +      assertTrue(checkSystemResourceConfigViaAdminPod(helper.adminServerPodName, helper.domainNamespace,
    +          "JDBCSystemResources/TestDataSource2/JDBCResource/JDBCDataSourceParams",
    +          "jdbc\\/TestDataSource2-2"), "JDBCSystemResource JNDIName not found");
    +    } else {
    +      int adminServiceNodePort
    +          = getServiceNodePort(helper.domainNamespace, getExternalServicePodName(helper.adminServerPodName), "default");
    +      assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid");
    +
    +      // create ingress for admin service
    +      // use traefik LB for kind cluster with ingress host header in url
    +      if (TestConstants.KIND_CLUSTER
    +          && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) {
    +        httpHostHeader = createIngressHostRouting(helper.domainNamespace, domainUid,
    +            helper.adminServerName, 7001);
    +        StringBuffer curlString = new StringBuffer("curl -g --user ");
    +        curlString.append(ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT)
    +            .append(" --noproxy '*' "
    +                + " -H 'host: " + httpHostHeader + "' " + " http://" + "localhost:"
    +                + TRAEFIK_INGRESS_HTTP_HOSTPORT)
    +            .append("/management/weblogic/latest/domainConfig")
    +            .append("/")
    +            .append("JDBCSystemResources/TestDataSource2/JDBCResource/JDBCDataSourceParams")
    +            .append("/");
    +
    +        logger.info("curl command {0}", new String(curlString));
    +
    +        assertTrue(Command
    +            .withParams(new CommandParams()
    +                .command(curlString.toString()))
    +            .executeAndVerify("jdbc\\/TestDataSource2-2"), "JDBCSystemResource JNDIName not found");
    +      } else {
    +        assertTrue(checkSystemResourceConfig(helper.adminSvcExtHost, adminServiceNodePort,
    +            "JDBCSystemResources/TestDataSource2/JDBCResource/JDBCDataSourceParams",
    +            "jdbc\\/TestDataSource2-2"), "JDBCSystemResource JNDIName not found");
    +
    +      }
    +    }
         logger.info("JDBCSystemResource configuration found");
     
    +
         // check that the domain status condition contains the correct type and expected reason
         logger.info("verifying the domain status condition contains the correct type and expected status");
         helper.verifyDomainStatusConditionNoErrorMsg("Completed", "True");
    @@ -363,13 +407,27 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() {
         verifyPodIntrospectVersionUpdated(pods.keySet(), introspectVersion, helper.domainNamespace);
     
         // check datasource configuration is deleted using REST api
    -    adminServiceNodePort
    -        = getServiceNodePort(helper.domainNamespace, getExternalServicePodName(helper.adminServerPodName), "default");
    -    assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid");
    -    assertFalse(checkSystemResourceConfig(helper.adminSvcExtHost, adminServiceNodePort, "JDBCSystemResources",
    -        "TestDataSource2"), "Found JDBCSystemResource datasource, should be deleted");
    +    if (OKE_CLUSTER) {
    +      assertFalse(checkSystemResourceConfigViaAdminPod(helper.adminServerPodName, helper.domainNamespace,
    +          "JDBCSystemResources",
    +          "TestDataSource2"), "Found JDBCSystemResource datasource, should be deleted");
    +    } else {
    +      int adminServiceNodePort
    +          = getServiceNodePort(helper.domainNamespace, getExternalServicePodName(helper.adminServerPodName), "default");
    +      assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid");
    +      if (TestConstants.KIND_CLUSTER
    +          && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) {
    +        assertFalse(checkSystemResourceConfig(helper.adminSvcExtHost, adminServiceNodePort, "JDBCSystemResources",
    +            "TestDataSource2"), "Found JDBCSystemResource datasource, should be deleted");
    +      } else {
    +        verifySystemResourceConfiguration(null, adminServiceNodePort,
    +            "JDBCSystemResources", "TestDataSource2", "404", httpHostHeader);
    +      }
    +    }
         logger.info("JDBCSystemResource Datasource is deleted");
     
    +
    +
         // check that the domain status condition contains the correct type and expected status
         logger.info("verifying the domain status condition contains the correct type and expected status");
         helper.verifyDomainStatusConditionNoErrorMsg("Completed", "True");
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java
    index 0881a5a2731..cbba7cfadb7 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java
    @@ -1082,7 +1082,7 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN
           int port = getServicePort(domainNamespace, adminServerPodName, "internal-t3");
           String domainName = adminServerPodName.split("-" + ADMIN_SERVER_NAME_BASE)[0];
           String serviceName = ADMIN_SERVER_NAME_BASE;
    -      String ingressName = domainNamespace + "-" + domainName + "-" + serviceName;
    +      String ingressName = domainNamespace + "-" + domainName + "-" + serviceName + "-" + port;
           String hostHeader = domainNamespace + "." + domainName + "." + serviceName;;
           Optional ingressFound;
           try {
    @@ -1256,7 +1256,7 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost,
           int port = getServicePort(domainNamespace, adminServerPodName, "internal-t3");
           String domainName = adminServerPodName.split("-" + ADMIN_SERVER_NAME_BASE)[0];
           String serviceName = ADMIN_SERVER_NAME_BASE;
    -      String ingressName = domainNamespace + "-" + domainName + "-" + serviceName;      
    +      String ingressName = domainNamespace + "-" + domainName + "-" + serviceName + "-" + port;
           String hostHeader = domainNamespace + "." + domainName + "." + serviceName;;
           Optional ingressFound;
           try {
    diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java
    index a530cc5a0d7..974a855878f 100644
    --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java
    +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java
    @@ -2365,7 +2365,7 @@ public static String createIngressHostRouting(String domainNamespace, String dom
                 .paths(Collections.singletonList(httpIngressPath)));
         ingressRules.add(ingressRule);
     
    -    String ingressName = domainNamespace + "-" + domainUid + "-" + serviceName;
    +    String ingressName = domainNamespace + "-" + domainUid + "-" + serviceName + '-' + port;
         assertDoesNotThrow(() -> createIngress(ingressName, domainNamespace, null,
             Files.readString(INGRESS_CLASS_FILE_NAME), ingressRules, null));
     
    
    From 7a3196208067aa4abc5d9f81e42d31de96d5df78 Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 15 Apr 2024 08:51:10 -0400
    Subject: [PATCH 031/356] Correct latestMinorVersion.html
    
    ---
     documentation/site/layouts/shortcodes/latestMinorVersion.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/documentation/site/layouts/shortcodes/latestMinorVersion.html b/documentation/site/layouts/shortcodes/latestMinorVersion.html
    index c51dcfd7737..f2febed08b7 100644
    --- a/documentation/site/layouts/shortcodes/latestMinorVersion.html
    +++ b/documentation/site/layouts/shortcodes/latestMinorVersion.html
    @@ -1 +1 @@
    -release/4.1
    \ No newline at end of file
    +release/4.2
    \ No newline at end of file
    
    From dc9964740cd26e977726909e91d5687b5408a484 Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 15 Apr 2024 09:16:40 -0400
    Subject: [PATCH 032/356] Dependency updates
    
    ---
     pom.xml | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/pom.xml b/pom.xml
    index 06a51d208a3..686ab6b5368 100644
    --- a/pom.xml
    +++ b/pom.xml
    @@ -662,7 +662,7 @@
         3.13.0
         3.1.1
         3.1.1
    -    3.3.0
    +    3.4.0
         3.3.1
         3.12.1
         3.2.5
    @@ -676,7 +676,7 @@
         10.15.0
         1.0
         3.3.2
    -    3.2.2
    +    3.2.3
         2.0.0.0
         1.3.3
         2.0.1
    @@ -715,8 +715,8 @@
         2.2
         2.10.1
         9.1.0
    -    2.0.12
    -    1.5.4
    +    2.0.13
    +    1.5.5
         ${project.basedir}/src-generated-swagger
         ${root-generated-swagger}/main/java
         ${project.basedir}/swagger/domain.json
    
    From e9429f3f603f69bf359978585b348abe7f96b84a Mon Sep 17 00:00:00 2001
    From: Ryan Eberhard 
    Date: Mon, 15 Apr 2024 17:08:40 +0000
    Subject: [PATCH 033/356] Merge branch 'readiness-get-action' into 'main'
    
    Support customization of readiness probe get action
    
    See merge request weblogic-cloud/weblogic-kubernetes-operator!4654
    
    (cherry picked from commit 7a76a1bd4548f74abc873bb062332d96db332a0b)
    
    c0658b20 Support customization of readiness probe get action
    68c9bc68 Update solution to use full standard probe schema
    2d20af9a Correct JSON documentation references
    2ccb976d Use builder to prevent modifying original
    ---
     documentation/domains/Cluster.json            |   113 +-
     documentation/domains/Cluster.md              |    61 +-
     documentation/domains/Domain.json             |   151 +-
     documentation/domains/Domain.md               |    97 +-
     documentation/domains/k8s1.13.5.md            |    30 +
     documentation/domains/k8s1.28.2.md            |   538 +
     .../json/KubernetesSchemaReference.java       |     4 +-
     .../json/caches/kubernetes-1.28.2.json        | 17775 ++++++++++++++++
     .../kubernetes/json/SchemaGeneratorTest.java  |     4 +-
     .../kubernetes/json/YamlDocGeneratorTest.java |     4 +-
     kubernetes/crd/cluster-crd.yaml               |   154 +-
     kubernetes/crd/domain-crd.yaml                |   468 +-
     operator/pom.xml                              |     6 +-
     .../operator/helpers/PodStepContext.java      |   151 +-
     .../processing/EffectiveServerSpec.java       |     8 +-
     .../weblogic/domain/ServerConfigurator.java   |     4 +-
     .../domain/model/BaseConfiguration.java       |    11 +-
     .../weblogic/domain/model/DomainResource.java |     2 +-
     .../model/EffectiveServerSpecCommonImpl.java  |     7 +-
     .../weblogic/domain/model/ProbeTuning.java    |   146 -
     .../weblogic/domain/model/ServerPod.java      |    79 +-
     .../weblogic/domain/model/Validator.java      |     5 +-
     .../operator/helpers/PodHelperTestBase.java   |     1 +
     .../model/DomainCommonConfigurator.java       |     6 +
     .../weblogic/domain/model/DomainV2Test.java   |     7 +-
     25 files changed, 19116 insertions(+), 716 deletions(-)
     create mode 100644 documentation/domains/k8s1.28.2.md
     create mode 100644 json-schema-generator/src/main/resources/oracle/kubernetes/json/caches/kubernetes-1.28.2.json
     delete mode 100644 operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ProbeTuning.java
    
    diff --git a/documentation/domains/Cluster.json b/documentation/domains/Cluster.json
    index 8cd87fc40cf..4bbd509d605 100644
    --- a/documentation/domains/Cluster.json
    +++ b/documentation/domains/Cluster.json
    @@ -165,35 +165,6 @@
         "Map": {
           "type": "object"
         },
    -    "ProbeTuning": {
    -      "type": "object",
    -      "properties": {
    -        "failureThreshold": {
    -          "default": 1,
    -          "description": "Number of times the check is performed before giving up. Giving up in case of liveness probe means restarting the container. In case of readiness probe, the Pod will be marked Unready. Defaults to 1.",
    -          "type": "integer",
    -          "minimum": 1
    -        },
    -        "periodSeconds": {
    -          "description": "The number of seconds between checks.",
    -          "type": "integer"
    -        },
    -        "timeoutSeconds": {
    -          "description": "The number of seconds with no response that indicates a failure.",
    -          "type": "integer"
    -        },
    -        "successThreshold": {
    -          "default": 1,
    -          "description": "Minimum number of times the check needs to pass for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness Probe.",
    -          "type": "integer",
    -          "minimum": 1
    -        },
    -        "initialDelaySeconds": {
    -          "description": "The number of seconds before the first check is performed.",
    -          "type": "integer"
    -        }
    -      }
    -    },
         "ServerPod": {
           "type": "object",
           "properties": {
    @@ -205,7 +176,7 @@
               "description": "If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to \"True\". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.PodReadinessGate"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodReadinessGate"
               }
             },
             "serviceAccountName": {
    @@ -214,7 +185,7 @@
             },
             "podSecurityContext": {
               "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext"
             },
             "priorityClassName": {
               "description": "If specified, indicates the Pod\u0027s priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be the default or zero, if there is no default. See `kubectl explain pods.spec.priorityClassName`.",
    @@ -231,7 +202,7 @@
               "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod\u0027s hosts file if specified. This is only valid for non-hostNetwork pods.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.HostAlias"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.HostAlias"
               }
             },
             "nodeSelector": {
    @@ -245,7 +216,7 @@
               "description": "Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
               }
             },
             "runtimeClassName": {
    @@ -256,22 +227,22 @@
               "description": "If specified, the Pod\u0027s tolerations. See `kubectl explain pods.spec.tolerations`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration"
               }
             },
             "readinessProbe": {
    -          "description": "Settings for the readiness probe associated with a WebLogic Server instance.",
    -          "$ref": "#/definitions/ProbeTuning"
    +          "description": "Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access.",
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe"
             },
             "containerSecurityContext": {
               "description": "Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext"
             },
             "envFrom": {
               "description": "List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the \u0027envFrom\u0027 source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource"
               }
             },
             "schedulerName": {
    @@ -283,32 +254,32 @@
               "type": "integer"
             },
             "livenessProbe": {
    -          "description": "Settings for the liveness probe associated with a WebLogic Server instance.",
    -          "$ref": "#/definitions/ProbeTuning"
    +          "description": "Settings for the liveness probe associated with a WebLogic Server instance. If not specified, the operator will create a probe that executes a script provided by the operator. The operator will also fill in any missing tuning-related fields, if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different script to execute.",
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe"
             },
             "topologySpreadConstraints": {
               "description": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed.",
               "type": "array",
               "items": {
    -            "$ref": "#/definitions/V1TopologySpreadConstraint"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.TopologySpreadConstraint"
               }
             },
             "volumes": {
               "description": "Additional volumes to be created in the server Pod. See `kubectl explain pods.spec.volumes`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Volume"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Volume"
               }
             },
             "resources": {
               "description": "Memory and CPU minimum requirements and limits for the WebLogic Server instance. See `kubectl explain pods.spec.containers.resources`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             },
             "env": {
               "description": "A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
               }
             },
             "restartPolicy": {
    @@ -330,14 +301,14 @@
               "description": "Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
               }
             },
             "initContainers": {
               "description": "Initialization containers to be included in the server Pod. See `kubectl explain pods.spec.initContainers`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
               }
             },
             "shutdown": {
    @@ -346,7 +317,7 @@
             },
             "affinity": {
               "description": "The Pod\u0027s scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/.  See `kubectl explain pods.spec.affinity`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity"
             }
           }
         },
    @@ -406,58 +377,12 @@
               "type": "boolean"
             }
           }
    -    },
    -    "V1TopologySpreadConstraint": {
    -      "description": "TopologySpreadConstraint specifies how to spread matching pods among the given topology.",
    -      "type": "object",
    -      "properties": {
    -        "nodeTaintsPolicy": {
    -          "description": "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.  If this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
    -          "type": "string"
    -        },
    -        "whenUnsatisfiable": {
    -          "description": "WhenUnsatisfiable indicates how to deal with a pod if it doesn\u0027t satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,   but giving higher precedence to topologies that would help reduce the   skew. A constraint is considered \"Unsatisfiable\" for an incoming pod if and only if every possible node assignment for that pod would violate \"MaxSkew\" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P |   P   |   P   | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won\u0027t make it *more* imbalanced. It\u0027s a required field.",
    -          "type": "string"
    -        },
    -        "maxSkew": {
    -          "description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable\u003dDoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | |  P P  |  P P  |   P   | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable\u003dScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It\u0027s a required field. Default value is 1 and 0 is not allowed.",
    -          "type": "integer"
    -        },
    -        "nodeAffinityPolicy": {
    -          "description": "NodeAffinityPolicy indicates how we will treat Pod\u0027s nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.  If this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
    -          "type": "string"
    -        },
    -        "labelSelector": {
    -          "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector"
    -        },
    -        "minDomains": {
    -          "description": "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won\u0027t schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.  For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | |  P P  |  P P  |  P P  | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.  This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default).",
    -          "type": "integer"
    -        },
    -        "topologyKey": {
    -          "description": "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each \u003ckey, value\u003e as a \"bucket\", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is \"kubernetes.io/hostname\", each Node is a domain of that topology. And, if TopologyKey is \"topology.kubernetes.io/zone\", each zone is a domain of that topology. It\u0027s a required field.",
    -          "type": "string"
    -        },
    -        "matchLabelKeys": {
    -          "description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn\u0027t set. Keys that don\u0027t exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.  This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).",
    -          "type": "array",
    -          "items": {
    -            "type": "string"
    -          }
    -        }
    -      },
    -      "required": [
    -        "maxSkew",
    -        "topologyKey",
    -        "whenUnsatisfiable"
    -      ]
         }
       },
       "properties": {
         "metadata": {
           "description": "The resource metadata. Must include the `name` and `namespace.",
    -      "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
    +      "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
         },
         "apiVersion": {
           "description": "The API version defines the versioned schema of this cluster.",
    diff --git a/documentation/domains/Cluster.md b/documentation/domains/Cluster.md
    index 69ab8558488..bc8173ac90d 100644
    --- a/documentation/domains/Cluster.md
    +++ b/documentation/domains/Cluster.md
    @@ -6,7 +6,7 @@ A Cluster resource describes the lifecycle options for all of the Managed Server
     | --- | --- | --- |
     | `apiVersion` | string | The API version defines the versioned schema of this cluster. |
     | `kind` | string | The type of the REST resource. Must be "Cluster". |
    -| `metadata` | [Object Meta](k8s1.13.5.md#object-meta) | The resource metadata. Must include the `name` and `namespace. |
    +| `metadata` | [Object Meta](k8s1.28.2.md#object-meta) | The resource metadata. Must include the `name` and `namespace. |
     | `spec` | [Cluster Spec](#cluster-spec) | The specification of the operation of the WebLogic cluster. Required. |
     | `status` | [Cluster Status](#cluster-status) | The current status of the operation of the WebLogic cluster. Updated automatically by the operator. |
     
    @@ -53,34 +53,34 @@ The specification of the operation of the WebLogic cluster. Required.
     
     | Name | Type | Description |
     | --- | --- | --- |
    -| `affinity` | [Affinity](k8s1.13.5.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/.  See `kubectl explain pods.spec.affinity`. |
    +| `affinity` | [Affinity](k8s1.28.2.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/.  See `kubectl explain pods.spec.affinity`. |
     | `annotations` | Map | The annotations to be added to generated resources. |
    -| `containers` | Array of [Container](k8s1.13.5.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. |
    -| `containerSecurityContext` | [Security Context](k8s1.13.5.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    -| `env` | Array of [Env Var](k8s1.13.5.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. |
    -| `envFrom` | Array of [Env From Source](k8s1.13.5.md#env-from-source) | List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. |
    -| `hostAliases` | Array of [Host Alias](k8s1.13.5.md#host-alias) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. |
    -| `initContainers` | Array of [Container](k8s1.13.5.md#container) | Initialization containers to be included in the server Pod. See `kubectl explain pods.spec.initContainers`. |
    +| `containers` | Array of [Container](k8s1.28.2.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. |
    +| `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    +| `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. |
    +| `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. |
    +| `hostAliases` | Array of [Host Alias](k8s1.28.2.md#host-alias) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. |
    +| `initContainers` | Array of [Container](k8s1.28.2.md#container) | Initialization containers to be included in the server Pod. See `kubectl explain pods.spec.initContainers`. |
     | `labels` | Map | The labels to be added to generated resources. The label names must not start with "weblogic.". |
    -| `livenessProbe` | [Probe Tuning](#probe-tuning) | Settings for the liveness probe associated with a WebLogic Server instance. |
    +| `livenessProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the liveness probe associated with a WebLogic Server instance. If not specified, the operator will create a probe that executes a script provided by the operator. The operator will also fill in any missing tuning-related fields, if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different script to execute. |
     | `maxPendingWaitTimeSeconds` | integer | The maximum time in seconds that the operator waits for a WebLogic Server pod to reach the running state before it considers the pod failed. Defaults to 5 minutes. |
     | `maxReadyWaitTimeSeconds` | integer | The maximum time in seconds that the operator waits for a WebLogic Server pod to reach the ready state before it considers the pod failed. Defaults to 1800 seconds. |
     | `nodeName` | string | NodeName is a request to schedule this Pod onto a specific Node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits the resource requirements. See `kubectl explain pods.spec.nodeName`. |
     | `nodeSelector` | Map | Selector which must match a Node's labels for the Pod to be scheduled on that Node. See `kubectl explain pods.spec.nodeSelector`. |
    -| `podSecurityContext` | [Pod Security Context](k8s1.13.5.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    +| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
     | `priorityClassName` | string | If specified, indicates the Pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be the default or zero, if there is no default. See `kubectl explain pods.spec.priorityClassName`. |
    -| `readinessGates` | Array of [Pod Readiness Gate](k8s1.13.5.md#pod-readiness-gate) | If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to "True". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md. |
    -| `readinessProbe` | [Probe Tuning](#probe-tuning) | Settings for the readiness probe associated with a WebLogic Server instance. |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the WebLogic Server instance. See `kubectl explain pods.spec.containers.resources`. |
    +| `readinessGates` | Array of [Pod Readiness Gate](k8s1.28.2.md#pod-readiness-gate) | If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to "True". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md. |
    +| `readinessProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access. |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the WebLogic Server instance. See `kubectl explain pods.spec.containers.resources`. |
     | `restartPolicy` | string | Restart policy for all containers within the Pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy. See `kubectl explain pods.spec.restartPolicy`. |
     | `runtimeClassName` | string | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this Pod. If no RuntimeClass resource matches the named class, the Pod will not be run. If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://github.com/kubernetes/community/blob/master/keps/sig-node/0014-runtime-class.md This is an alpha feature and may change in the future. See `kubectl explain pods.spec.runtimeClassName`. |
     | `schedulerName` | string | If specified, the Pod will be dispatched by the specified scheduler. If not specified, the Pod will be dispatched by the default scheduler. See `kubectl explain pods.spec.schedulerName`. |
     | `serviceAccountName` | string | Name of the ServiceAccount to be used to run this Pod. If it is not set, default ServiceAccount will be used. The ServiceAccount has to exist at the time the Pod is created. See `kubectl explain pods.spec.serviceAccountName`. |
     | `shutdown` | [Shutdown](#shutdown) | Configures how the operator should shut down the server instance. |
    -| `tolerations` | Array of [Toleration](k8s1.13.5.md#toleration) | If specified, the Pod's tolerations. See `kubectl explain pods.spec.tolerations`. |
    -| `topologySpreadConstraints` | Array of [V 1 Topology Spread Constraint](#v-1-topology-spread-constraint) | TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. |
    -| `volumeMounts` | Array of [Volume Mount](k8s1.13.5.md#volume-mount) | Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`. |
    -| `volumes` | Array of [Volume](k8s1.13.5.md#volume) | Additional volumes to be created in the server Pod. See `kubectl explain pods.spec.volumes`. |
    +| `tolerations` | Array of [Toleration](k8s1.28.2.md#toleration) | If specified, the Pod's tolerations. See `kubectl explain pods.spec.tolerations`. |
    +| `topologySpreadConstraints` | Array of [Topology Spread Constraint](k8s1.28.2.md#topology-spread-constraint) | TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. |
    +| `volumeMounts` | Array of [Volume Mount](k8s1.28.2.md#volume-mount) | Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`. |
    +| `volumes` | Array of [Volume](k8s1.28.2.md#volume) | Additional volumes to be created in the server Pod. See `kubectl explain pods.spec.volumes`. |
     
     ### Server Service
     
    @@ -99,16 +99,6 @@ The specification of the operation of the WebLogic cluster. Required.
     | `status` | string | The status of the condition. Can be True, False. |
     | `type` | string | The type of the condition. Valid types are Completed, Available, Failed, and Rolling. |
     
    -### Probe Tuning
    -
    -| Name | Type | Description |
    -| --- | --- | --- |
    -| `failureThreshold` | integer | Number of times the check is performed before giving up. Giving up in case of liveness probe means restarting the container. In case of readiness probe, the Pod will be marked Unready. Defaults to 1. |
    -| `initialDelaySeconds` | integer | The number of seconds before the first check is performed. |
    -| `periodSeconds` | integer | The number of seconds between checks. |
    -| `successThreshold` | integer | Minimum number of times the check needs to pass for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness Probe. |
    -| `timeoutSeconds` | integer | The number of seconds with no response that indicates a failure. |
    -
     ### Shutdown
     
     | Name | Type | Description |
    @@ -117,19 +107,4 @@ The specification of the operation of the WebLogic cluster. Required.
     | `shutdownType` | string | Specifies how the operator will shut down server instances. Legal values are `Graceful` and `Forced`. Defaults to `Graceful`. |
     | `skipWaitingCohEndangeredState` | Boolean | For graceful shutdown only, set to true to skip waiting for Coherence Cache Cluster service MBean HAStatus in safe state before shutdown. By default, the operator will wait until it is safe to shutdown the Coherence Cache Cluster. Defaults to false. |
     | `timeoutSeconds` | integer | For graceful shutdown only, number of seconds to wait before aborting in-flight work and shutting down the server. Defaults to 30 seconds. |
    -| `waitForAllSessions` | Boolean | For graceful shutdown only, set to true to wait for all HTTP sessions during in-flight work handling; false to wait for non-persisted HTTP sessions only. Defaults to false. |
    -
    -### V 1 Topology Spread Constraint
    -
    -TopologySpreadConstraint specifies how to spread matching pods among the given topology.
    -
    -| Name | Type | Description |
    -| --- | --- | --- |
    -| `labelSelector` | [Label Selector](k8s1.13.5.md#label-selector) | A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. |
    -| `matchLabelKeys` | Array of string | MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.  This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). |
    -| `maxSkew` | integer | MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | |  P P  |  P P  |   P   | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed. |
    -| `minDomains` | integer | MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.  For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | |  P P  |  P P  |  P P  | The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.  This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). |
    -| `nodeAffinityPolicy` | string | NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.  If this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. |
    -| `nodeTaintsPolicy` | string | NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.  If this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. |
    -| `topologyKey` | string | TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each  as a "bucket", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. It's a required field. |
    -| `whenUnsatisfiable` | string | WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,   but giving higher precedence to topologies that would help reduce the   skew. A constraint is considered "Unsatisfiable" for an incoming pod if and only if every possible node assignment for that pod would violate "MaxSkew" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P |   P   |   P   | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field. |
    \ No newline at end of file
    +| `waitForAllSessions` | Boolean | For graceful shutdown only, set to true to wait for all HTTP sessions during in-flight work handling; false to wait for non-persisted HTTP sessions only. Defaults to false. |
    \ No newline at end of file
    diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json
    index 2814b2d85d8..6e52837c257 100644
    --- a/documentation/domains/Domain.json
    +++ b/documentation/domains/Domain.json
    @@ -367,7 +367,7 @@
               "description": "A list of image pull Secrets for the WebLogic Server image.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.LocalObjectReference"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.LocalObjectReference"
               }
             },
             "fluentdSpecification": {
    @@ -426,7 +426,7 @@
             },
             "webLogicCredentialsSecret": {
               "description": "Reference to a Kubernetes Secret that contains the user name and password needed to boot a WebLogic Server under the `username` and `password` fields.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.LocalObjectReference"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.LocalObjectReference"
             },
             "adminServer": {
               "description": "Lifecycle options for the Administration Server, including Java options, environment variables, additional Pod content, and which channels or network access points should be exposed using a NodePort Service.",
    @@ -461,7 +461,7 @@
               "description": "References to Cluster resources that describe the lifecycle options for all of the Managed Server members of a WebLogic cluster, including Java options, environment variables, additional Pod content, and the ability to explicitly start, stop, or restart cluster members. The Cluster resource must describe a cluster that already exists in the WebLogic domain configuration.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.LocalObjectReference"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.LocalObjectReference"
               }
             },
             "image": {
    @@ -621,7 +621,7 @@
             },
             "resources": {
               "description": "Memory and CPU minimum requirements and limits for the fluentbit container. See `kubectl explain pods.spec.containers.resources`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             },
             "containerArgs": {
               "description": "(Optional) The Fluentbit sidecar container spec\u0027s args. Default is: [ -c, /etc/fluent-bit.conf ] if not specified",
    @@ -642,7 +642,7 @@
               "description": "A list of environment variables to set in the fluentbit container. See `kubectl explain pods.spec.containers.env`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
               }
             },
             "containerCommand": {
    @@ -656,7 +656,7 @@
               "description": "Volume mounts for fluentbit container",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
               }
             }
           }
    @@ -683,7 +683,7 @@
             },
             "resources": {
               "description": "Memory and CPU minimum requirements and limits for the fluentd container. See `kubectl explain pods.spec.containers.resources`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             },
             "containerArgs": {
               "description": "(Optional) The Fluentd sidecar container spec\u0027s args. Default is: [ -c, /etc/fluentd.conf ] if not specified",
    @@ -700,7 +700,7 @@
               "description": "A list of environment variables to set in the fluentd container. See `kubectl explain pods.spec.containers.env`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
               }
             },
             "containerCommand": {
    @@ -714,7 +714,7 @@
               "description": "Volume mounts for fluentd container",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
               }
             }
           }
    @@ -762,24 +762,24 @@
           "properties": {
             "podSecurityContext": {
               "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext"
             },
             "resources": {
               "description": "Memory and CPU minimum requirements and limits for the Introspector Job Pod. See `kubectl explain pods.spec.containers.resources`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             },
             "env": {
               "description": "A list of environment variables to set in the Introspector Job Pod container. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
               }
             },
             "envFrom": {
               "description": "List of sources to populate environment variables in the Introspector Job Pod container. The sources include either a config map or a secret. The operator will not expand the dependent variables in the \u0027envFrom\u0027 source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource"
               }
             }
           }
    @@ -899,7 +899,7 @@
             },
             "resources": {
               "description": "Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             }
           }
         },
    @@ -943,7 +943,7 @@
           "properties": {
             "metadata": {
               "description": "The PersistentVolume metadata. Must include the `name` field. Required.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
             },
             "spec": {
               "description": "The specification of a persistent volume for `Domain on PV` domain. Required. This section provides a subset of fields in standard Kubernetes PersistentVolume specifications.",
    @@ -956,7 +956,7 @@
           "properties": {
             "metadata": {
               "description": "The PersistentVolumeClaim metadata. Must include the `name` field. Required.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
             },
             "spec": {
               "description": "The specifications of a persistent volume claim for `Domain on PV` domain. Required. This section provides a subset of fields in standard Kubernetes PersistentVolumeClaim specifications.",
    @@ -977,7 +977,7 @@
             },
             "resources": {
               "description": "Resources represents the minimum resources the volume should have. More info https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources. ResourceRequirements describes the compute resource requirements.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             }
           }
         },
    @@ -990,7 +990,7 @@
             },
             "nfs": {
               "description": "nfs represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs. Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.NFSVolumeSource"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.NFSVolumeSource"
             },
             "persistentVolumeReclaimPolicy": {
               "description": "PersistentVolumeReclaimPolicy defines what happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming",
    @@ -998,7 +998,7 @@
             },
             "hostPath": {
               "description": "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath. Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.HostPathVolumeSource"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.HostPathVolumeSource"
             },
             "capacity": {
               "description": "Capacity is the description of the persistent volume\u0027s resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity",
    @@ -1006,35 +1006,6 @@
             }
           }
         },
    -    "ProbeTuning": {
    -      "type": "object",
    -      "properties": {
    -        "failureThreshold": {
    -          "default": 1,
    -          "description": "Number of times the check is performed before giving up. Giving up in case of liveness probe means restarting the container. In case of readiness probe, the Pod will be marked Unready. Defaults to 1.",
    -          "type": "integer",
    -          "minimum": 1
    -        },
    -        "periodSeconds": {
    -          "description": "The number of seconds between checks.",
    -          "type": "integer"
    -        },
    -        "timeoutSeconds": {
    -          "description": "The number of seconds with no response that indicates a failure.",
    -          "type": "integer"
    -        },
    -        "successThreshold": {
    -          "default": 1,
    -          "description": "Minimum number of times the check needs to pass for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness Probe.",
    -          "type": "integer",
    -          "minimum": 1
    -        },
    -        "initialDelaySeconds": {
    -          "description": "The number of seconds before the first check is performed.",
    -          "type": "integer"
    -        }
    -      }
    -    },
         "ServerHealth": {
           "type": "object",
           "properties": {
    @@ -1066,7 +1037,7 @@
               "description": "If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to \"True\". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.PodReadinessGate"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodReadinessGate"
               }
             },
             "serviceAccountName": {
    @@ -1075,7 +1046,7 @@
             },
             "podSecurityContext": {
               "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext"
             },
             "priorityClassName": {
               "description": "If specified, indicates the Pod\u0027s priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be the default or zero, if there is no default. See `kubectl explain pods.spec.priorityClassName`.",
    @@ -1092,7 +1063,7 @@
               "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod\u0027s hosts file if specified. This is only valid for non-hostNetwork pods.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.HostAlias"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.HostAlias"
               }
             },
             "nodeSelector": {
    @@ -1106,7 +1077,7 @@
               "description": "Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.VolumeMount"
               }
             },
             "runtimeClassName": {
    @@ -1117,22 +1088,22 @@
               "description": "If specified, the Pod\u0027s tolerations. See `kubectl explain pods.spec.tolerations`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration"
               }
             },
             "readinessProbe": {
    -          "description": "Settings for the readiness probe associated with a WebLogic Server instance.",
    -          "$ref": "#/definitions/ProbeTuning"
    +          "description": "Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access.",
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe"
             },
             "containerSecurityContext": {
               "description": "Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext"
             },
             "envFrom": {
               "description": "List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the \u0027envFrom\u0027 source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource"
               }
             },
             "schedulerName": {
    @@ -1144,32 +1115,32 @@
               "type": "integer"
             },
             "livenessProbe": {
    -          "description": "Settings for the liveness probe associated with a WebLogic Server instance.",
    -          "$ref": "#/definitions/ProbeTuning"
    +          "description": "Settings for the liveness probe associated with a WebLogic Server instance. If not specified, the operator will create a probe that executes a script provided by the operator. The operator will also fill in any missing tuning-related fields, if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different script to execute.",
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe"
             },
             "topologySpreadConstraints": {
               "description": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed.",
               "type": "array",
               "items": {
    -            "$ref": "#/definitions/V1TopologySpreadConstraint"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.TopologySpreadConstraint"
               }
             },
             "volumes": {
               "description": "Additional volumes to be created in the server Pod. See `kubectl explain pods.spec.volumes`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Volume"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Volume"
               }
             },
             "resources": {
               "description": "Memory and CPU minimum requirements and limits for the WebLogic Server instance. See `kubectl explain pods.spec.containers.resources`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.ResourceRequirements"
             },
             "env": {
               "description": "A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvVar"
               }
             },
             "restartPolicy": {
    @@ -1191,14 +1162,14 @@
               "description": "Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
               }
             },
             "initContainers": {
               "description": "Initialization containers to be included in the server Pod. See `kubectl explain pods.spec.initContainers`.",
               "type": "array",
               "items": {
    -            "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
    +            "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Container"
               }
             },
             "shutdown": {
    @@ -1207,7 +1178,7 @@
             },
             "affinity": {
               "description": "The Pod\u0027s scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/.  See `kubectl explain pods.spec.affinity`.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity"
    +          "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Affinity"
             }
           }
         },
    @@ -1325,52 +1296,6 @@
             }
           }
         },
    -    "V1TopologySpreadConstraint": {
    -      "description": "TopologySpreadConstraint specifies how to spread matching pods among the given topology.",
    -      "type": "object",
    -      "properties": {
    -        "nodeTaintsPolicy": {
    -          "description": "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.  If this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
    -          "type": "string"
    -        },
    -        "whenUnsatisfiable": {
    -          "description": "WhenUnsatisfiable indicates how to deal with a pod if it doesn\u0027t satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,   but giving higher precedence to topologies that would help reduce the   skew. A constraint is considered \"Unsatisfiable\" for an incoming pod if and only if every possible node assignment for that pod would violate \"MaxSkew\" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P |   P   |   P   | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won\u0027t make it *more* imbalanced. It\u0027s a required field.",
    -          "type": "string"
    -        },
    -        "maxSkew": {
    -          "description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable\u003dDoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | |  P P  |  P P  |   P   | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable\u003dScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It\u0027s a required field. Default value is 1 and 0 is not allowed.",
    -          "type": "integer"
    -        },
    -        "nodeAffinityPolicy": {
    -          "description": "NodeAffinityPolicy indicates how we will treat Pod\u0027s nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.  If this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
    -          "type": "string"
    -        },
    -        "labelSelector": {
    -          "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
    -          "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector"
    -        },
    -        "minDomains": {
    -          "description": "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won\u0027t schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.  For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | |  P P  |  P P  |  P P  | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.  This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default).",
    -          "type": "integer"
    -        },
    -        "topologyKey": {
    -          "description": "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each \u003ckey, value\u003e as a \"bucket\", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is \"kubernetes.io/hostname\", each Node is a domain of that topology. And, if TopologyKey is \"topology.kubernetes.io/zone\", each zone is a domain of that topology. It\u0027s a required field.",
    -          "type": "string"
    -        },
    -        "matchLabelKeys": {
    -          "description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn\u0027t set. Keys that don\u0027t exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.  This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).",
    -          "type": "array",
    -          "items": {
    -            "type": "string"
    -          }
    -        }
    -      },
    -      "required": [
    -        "maxSkew",
    -        "topologyKey",
    -        "whenUnsatisfiable"
    -      ]
    -    },
         "WDTTimeouts": {
           "type": "object",
           "properties": {
    @@ -1420,7 +1345,7 @@
       "properties": {
         "metadata": {
           "description": "The resource metadata. Must include the `name` and `namespace`. Required.",
    -      "$ref": "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.13.5/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
    +      "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
         },
         "apiVersion": {
           "description": "The API version defines the versioned schema of this Domain. Required.",
    diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md
    index a3f84cac118..69353f18487 100644
    --- a/documentation/domains/Domain.md
    +++ b/documentation/domains/Domain.md
    @@ -6,7 +6,7 @@ A Domain resource describes the configuration, logging, images, and lifecycle of
     | --- | --- | --- |
     | `apiVersion` | string | The API version defines the versioned schema of this Domain. Required. |
     | `kind` | string | The type of the REST resource. Must be "Domain". Required. |
    -| `metadata` | [Object Meta](k8s1.13.5.md#object-meta) | The resource metadata. Must include the `name` and `namespace`. Required. |
    +| `metadata` | [Object Meta](k8s1.28.2.md#object-meta) | The resource metadata. Must include the `name` and `namespace`. Required. |
     | `spec` | [Domain Spec](#domain-spec) | The specification of the operation of the WebLogic domain. Required. |
     | `status` | [Domain Status](#domain-status) | The current status of the operation of the WebLogic domain. Updated automatically by the operator. |
     
    @@ -17,7 +17,7 @@ The specification of the operation of the WebLogic domain. Required.
     | Name | Type | Description |
     | --- | --- | --- |
     | `adminServer` | [Admin Server](#admin-server) | Lifecycle options for the Administration Server, including Java options, environment variables, additional Pod content, and which channels or network access points should be exposed using a NodePort Service. |
    -| `clusters` | Array of [Local Object Reference](k8s1.13.5.md#local-object-reference) | References to Cluster resources that describe the lifecycle options for all of the Managed Server members of a WebLogic cluster, including Java options, environment variables, additional Pod content, and the ability to explicitly start, stop, or restart cluster members. The Cluster resource must describe a cluster that already exists in the WebLogic domain configuration. |
    +| `clusters` | Array of [Local Object Reference](k8s1.28.2.md#local-object-reference) | References to Cluster resources that describe the lifecycle options for all of the Managed Server members of a WebLogic cluster, including Java options, environment variables, additional Pod content, and the ability to explicitly start, stop, or restart cluster members. The Cluster resource must describe a cluster that already exists in the WebLogic domain configuration. |
     | `configuration` | [Configuration](#configuration) | Models and overrides affecting the WebLogic domain configuration. |
     | `dataHome` | string | An optional directory in a server's container for data storage of default and custom file stores. If `dataHome` is not specified or its value is either not set or empty, then the data storage directories are determined from the WebLogic domain configuration. |
     | `domainHome` | string | The directory containing the WebLogic domain configuration inside the container. Defaults to /shared/domains/ if `domainHomeSourceType` is PersistentVolume. Defaults to /u01/oracle/user_projects/domains/ if `domainHomeSourceType` is Image. Defaults to /u01/domains/ if `domainHomeSourceType` is FromModel. |
    @@ -30,7 +30,7 @@ The specification of the operation of the WebLogic domain. Required.
     | `httpAccessLogInLogHome` | Boolean | Specifies whether the server HTTP access log files will be written to the same directory specified in `logHome`. Otherwise, server HTTP access log files will be written to the directory configured in the WebLogic domain configuration. Defaults to true. |
     | `image` | string | The WebLogic Server image; required when `domainHomeSourceType` is Image or FromModel; otherwise, defaults to container-registry.oracle.com/middleware/weblogic:12.2.1.4. |
     | `imagePullPolicy` | string | The image pull policy for the WebLogic Server image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. |
    -| `imagePullSecrets` | Array of [Local Object Reference](k8s1.13.5.md#local-object-reference) | A list of image pull Secrets for the WebLogic Server image. |
    +| `imagePullSecrets` | Array of [Local Object Reference](k8s1.28.2.md#local-object-reference) | A list of image pull Secrets for the WebLogic Server image. |
     | `includeServerOutInPodLog` | Boolean | Specifies whether the server .out file will be included in the Pod's log. Defaults to true. |
     | `introspector` | [Introspector](#introspector) | Lifecycle options for the Introspector Job Pod, including Java options, environment variables, and resources. |
     | `introspectVersion` | string | Changes to this field cause the operator to repeat its introspection of the WebLogic domain configuration. Repeating introspection is required for the operator to recognize changes to the domain configuration, such as adding a new WebLogic cluster or Managed Server instance, to regenerate configuration overrides, or to regenerate the WebLogic domain home when the `domainHomeSourceType` is `FromModel`. Introspection occurs automatically, without requiring change to this field, when servers are first started or restarted after a full domain shut down. For the `FromModel` `domainHomeSourceType`, introspection also occurs when a running server must be restarted because of changes to any of the fields listed here: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-lifecycle/startup/#properties-that-cause-servers-to-be-restarted. The introspectVersion value must be a valid label value in Kubernetes. See also `domains.spec.configuration.overrideDistributionStrategy`. |
    @@ -49,7 +49,7 @@ The specification of the operation of the WebLogic domain. Required.
     | `serverPod` | [Server Pod](#server-pod) | Customization affecting the generation of Pods for WebLogic Server instances. |
     | `serverService` | [Server Service](#server-service) | Customization affecting the generation of ClusterIP Services for WebLogic Server instances. |
     | `serverStartPolicy` | string | The strategy for deciding whether to start a WebLogic Server instance. Legal values are AdminOnly, Never, or IfNeeded. Defaults to IfNeeded. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-lifecycle/startup/#starting-and-stopping-servers. |
    -| `webLogicCredentialsSecret` | [Local Object Reference](k8s1.13.5.md#local-object-reference) | Reference to a Kubernetes Secret that contains the user name and password needed to boot a WebLogic Server under the `username` and `password` fields. |
    +| `webLogicCredentialsSecret` | [Local Object Reference](k8s1.28.2.md#local-object-reference) | Reference to a Kubernetes Secret that contains the user name and password needed to boot a WebLogic Server under the `username` and `password` fields. |
     
     ### Domain Status
     
    @@ -100,13 +100,13 @@ The current status of the operation of the WebLogic domain. Updated automaticall
     | `containerArgs` | Array of string | (Optional) The Fluentbit sidecar container spec's args. Default is: [ -c, /etc/fluent-bit.conf ] if not specified |
     | `containerCommand` | Array of string | (Optional) The Fluentbit sidecar container spec's command. Default is not set if not specified |
     | `elasticSearchCredentials` | string | Fluentbit elastic search credentials. A Kubernetes secret in the same namespace of the domain. It must contains 4 keys: elasticsearchhost - ElasticSearch Host Service Address, elasticsearchport - Elastic Search Service Port, elasticsearchuser - Elastic Search Service User Name, elasticsearchpassword - Elastic Search User Password |
    -| `env` | Array of [Env Var](k8s1.13.5.md#env-var) | A list of environment variables to set in the fluentbit container. See `kubectl explain pods.spec.containers.env`. |
    +| `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the fluentbit container. See `kubectl explain pods.spec.containers.env`. |
     | `fluentbitConfiguration` | string | The Fluentbit configuration text, specify your own custom fluentbit configuration. |
     | `image` | string | The Fluentbit container image name. Defaults to fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2 |
     | `imagePullPolicy` | string | The image pull policy for the Fluentbit sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. |
     | `parserConfiguration` | string | The Fluentbit parser configuration text, specify your own custom fluentbit configuration. |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the fluentbit container. See `kubectl explain pods.spec.containers.resources`. |
    -| `volumeMounts` | Array of [Volume Mount](k8s1.13.5.md#volume-mount) | Volume mounts for fluentbit container |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the fluentbit container. See `kubectl explain pods.spec.containers.resources`. |
    +| `volumeMounts` | Array of [Volume Mount](k8s1.28.2.md#volume-mount) | Volume mounts for fluentbit container |
     | `watchIntrospectorLogs` | Boolean | Fluentbit will watch introspector logs |
     
     ### Fluentd Specification
    @@ -116,12 +116,12 @@ The current status of the operation of the WebLogic domain. Updated automaticall
     | `containerArgs` | Array of string | (Optional) The Fluentd sidecar container spec's args. Default is: [ -c, /etc/fluentd.conf ] if not specified |
     | `containerCommand` | Array of string | (Optional) The Fluentd sidecar container spec's command. Default is not set if not specified |
     | `elasticSearchCredentials` | string | Fluentd elastic search credentials. A Kubernetes secret in the same namespace of the domain. It must contains 4 keys: elasticsearchhost - ElasticSearch Host Service Address, elasticsearchport - Elastic Search Service Port, elasticsearchuser - Elastic Search Service User Name, elasticsearchpassword - Elastic Search User Password |
    -| `env` | Array of [Env Var](k8s1.13.5.md#env-var) | A list of environment variables to set in the fluentd container. See `kubectl explain pods.spec.containers.env`. |
    +| `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the fluentd container. See `kubectl explain pods.spec.containers.env`. |
     | `fluentdConfiguration` | string | The fluentd configuration text, specify your own custom fluentd configuration. |
     | `image` | string | The Fluentd container image name. Defaults to fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2 |
     | `imagePullPolicy` | string | The image pull policy for the Fluentd sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the fluentd container. See `kubectl explain pods.spec.containers.resources`. |
    -| `volumeMounts` | Array of [Volume Mount](k8s1.13.5.md#volume-mount) | Volume mounts for fluentd container |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the fluentd container. See `kubectl explain pods.spec.containers.resources`. |
    +| `volumeMounts` | Array of [Volume Mount](k8s1.28.2.md#volume-mount) | Volume mounts for fluentd container |
     | `watchIntrospectorLogs` | Boolean | Fluentd will watch introspector logs |
     
     ### Introspector
    @@ -148,40 +148,40 @@ The current status of the operation of the WebLogic domain. Updated automaticall
     | `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 |
     | `imagePullPolicy` | string | The image pull policy for the WebLogic Monitoring Exporter sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. |
     | `port` | integer | The port exposed by the WebLogic Monitoring Exporter running in the sidecar container. Defaults to 8080. The port value must not conflict with a port used by any WebLogic Server instance, including the ports of built-in channels or network access points (NAPs). |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. |
     
     ### Server Pod
     
     | Name | Type | Description |
     | --- | --- | --- |
    -| `affinity` | [Affinity](k8s1.13.5.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/.  See `kubectl explain pods.spec.affinity`. |
    +| `affinity` | [Affinity](k8s1.28.2.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/.  See `kubectl explain pods.spec.affinity`. |
     | `annotations` | Map | The annotations to be added to generated resources. |
    -| `containers` | Array of [Container](k8s1.13.5.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. |
    -| `containerSecurityContext` | [Security Context](k8s1.13.5.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    -| `env` | Array of [Env Var](k8s1.13.5.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. |
    -| `envFrom` | Array of [Env From Source](k8s1.13.5.md#env-from-source) | List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. |
    -| `hostAliases` | Array of [Host Alias](k8s1.13.5.md#host-alias) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. |
    -| `initContainers` | Array of [Container](k8s1.13.5.md#container) | Initialization containers to be included in the server Pod. See `kubectl explain pods.spec.initContainers`. |
    +| `containers` | Array of [Container](k8s1.28.2.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. |
    +| `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    +| `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. |
    +| `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. |
    +| `hostAliases` | Array of [Host Alias](k8s1.28.2.md#host-alias) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. |
    +| `initContainers` | Array of [Container](k8s1.28.2.md#container) | Initialization containers to be included in the server Pod. See `kubectl explain pods.spec.initContainers`. |
     | `labels` | Map | The labels to be added to generated resources. The label names must not start with "weblogic.". |
    -| `livenessProbe` | [Probe Tuning](#probe-tuning) | Settings for the liveness probe associated with a WebLogic Server instance. |
    +| `livenessProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the liveness probe associated with a WebLogic Server instance. If not specified, the operator will create a probe that executes a script provided by the operator. The operator will also fill in any missing tuning-related fields, if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different script to execute. |
     | `maxPendingWaitTimeSeconds` | integer | The maximum time in seconds that the operator waits for a WebLogic Server pod to reach the running state before it considers the pod failed. Defaults to 5 minutes. |
     | `maxReadyWaitTimeSeconds` | integer | The maximum time in seconds that the operator waits for a WebLogic Server pod to reach the ready state before it considers the pod failed. Defaults to 1800 seconds. |
     | `nodeName` | string | NodeName is a request to schedule this Pod onto a specific Node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits the resource requirements. See `kubectl explain pods.spec.nodeName`. |
     | `nodeSelector` | Map | Selector which must match a Node's labels for the Pod to be scheduled on that Node. See `kubectl explain pods.spec.nodeSelector`. |
    -| `podSecurityContext` | [Pod Security Context](k8s1.13.5.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    +| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
     | `priorityClassName` | string | If specified, indicates the Pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be the default or zero, if there is no default. See `kubectl explain pods.spec.priorityClassName`. |
    -| `readinessGates` | Array of [Pod Readiness Gate](k8s1.13.5.md#pod-readiness-gate) | If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to "True". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md. |
    -| `readinessProbe` | [Probe Tuning](#probe-tuning) | Settings for the readiness probe associated with a WebLogic Server instance. |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the WebLogic Server instance. See `kubectl explain pods.spec.containers.resources`. |
    +| `readinessGates` | Array of [Pod Readiness Gate](k8s1.28.2.md#pod-readiness-gate) | If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to "True". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md. |
    +| `readinessProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access. |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the WebLogic Server instance. See `kubectl explain pods.spec.containers.resources`. |
     | `restartPolicy` | string | Restart policy for all containers within the Pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy. See `kubectl explain pods.spec.restartPolicy`. |
     | `runtimeClassName` | string | RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this Pod. If no RuntimeClass resource matches the named class, the Pod will not be run. If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://github.com/kubernetes/community/blob/master/keps/sig-node/0014-runtime-class.md This is an alpha feature and may change in the future. See `kubectl explain pods.spec.runtimeClassName`. |
     | `schedulerName` | string | If specified, the Pod will be dispatched by the specified scheduler. If not specified, the Pod will be dispatched by the default scheduler. See `kubectl explain pods.spec.schedulerName`. |
     | `serviceAccountName` | string | Name of the ServiceAccount to be used to run this Pod. If it is not set, default ServiceAccount will be used. The ServiceAccount has to exist at the time the Pod is created. See `kubectl explain pods.spec.serviceAccountName`. |
     | `shutdown` | [Shutdown](#shutdown) | Configures how the operator should shut down the server instance. |
    -| `tolerations` | Array of [Toleration](k8s1.13.5.md#toleration) | If specified, the Pod's tolerations. See `kubectl explain pods.spec.tolerations`. |
    -| `topologySpreadConstraints` | Array of [V 1 Topology Spread Constraint](#v-1-topology-spread-constraint) | TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. |
    -| `volumeMounts` | Array of [Volume Mount](k8s1.13.5.md#volume-mount) | Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`. |
    -| `volumes` | Array of [Volume](k8s1.13.5.md#volume) | Additional volumes to be created in the server Pod. See `kubectl explain pods.spec.volumes`. |
    +| `tolerations` | Array of [Toleration](k8s1.28.2.md#toleration) | If specified, the Pod's tolerations. See `kubectl explain pods.spec.tolerations`. |
    +| `topologySpreadConstraints` | Array of [Topology Spread Constraint](k8s1.28.2.md#topology-spread-constraint) | TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. |
    +| `volumeMounts` | Array of [Volume Mount](k8s1.28.2.md#volume-mount) | Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`. |
    +| `volumes` | Array of [Volume](k8s1.28.2.md#volume) | Additional volumes to be created in the server Pod. See `kubectl explain pods.spec.volumes`. |
     
     ### Server Service
     
    @@ -275,20 +275,10 @@ The current status of the operation of the WebLogic domain. Updated automaticall
     
     | Name | Type | Description |
     | --- | --- | --- |
    -| `env` | Array of [Env Var](k8s1.13.5.md#env-var) | A list of environment variables to set in the Introspector Job Pod container. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. |
    -| `envFrom` | Array of [Env From Source](k8s1.13.5.md#env-from-source) | List of sources to populate environment variables in the Introspector Job Pod container. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. |
    -| `podSecurityContext` | [Pod Security Context](k8s1.13.5.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Introspector Job Pod. See `kubectl explain pods.spec.containers.resources`. |
    -
    -### Probe Tuning
    -
    -| Name | Type | Description |
    -| --- | --- | --- |
    -| `failureThreshold` | integer | Number of times the check is performed before giving up. Giving up in case of liveness probe means restarting the container. In case of readiness probe, the Pod will be marked Unready. Defaults to 1. |
    -| `initialDelaySeconds` | integer | The number of seconds before the first check is performed. |
    -| `periodSeconds` | integer | The number of seconds between checks. |
    -| `successThreshold` | integer | Minimum number of times the check needs to pass for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness Probe. |
    -| `timeoutSeconds` | integer | The number of seconds with no response that indicates a failure. |
    +| `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the Introspector Job Pod container. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. |
    +| `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the Introspector Job Pod container. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. |
    +| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Introspector Job Pod. See `kubectl explain pods.spec.containers.resources`. |
     
     ### Shutdown
     
    @@ -300,21 +290,6 @@ The current status of the operation of the WebLogic domain. Updated automaticall
     | `timeoutSeconds` | integer | For graceful shutdown only, number of seconds to wait before aborting in-flight work and shutting down the server. Defaults to 30 seconds. |
     | `waitForAllSessions` | Boolean | For graceful shutdown only, set to true to wait for all HTTP sessions during in-flight work handling; false to wait for non-persisted HTTP sessions only. Defaults to false. |
     
    -### V 1 Topology Spread Constraint
    -
    -TopologySpreadConstraint specifies how to spread matching pods among the given topology.
    -
    -| Name | Type | Description |
    -| --- | --- | --- |
    -| `labelSelector` | [Label Selector](k8s1.13.5.md#label-selector) | A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. |
    -| `matchLabelKeys` | Array of string | MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.  This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). |
    -| `maxSkew` | integer | MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | |  P P  |  P P  |   P   | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed. |
    -| `minDomains` | integer | MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.  For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | |  P P  |  P P  |  P P  | The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.  This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). |
    -| `nodeAffinityPolicy` | string | NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.  If this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. |
    -| `nodeTaintsPolicy` | string | NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.  If this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. |
    -| `topologyKey` | string | TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each  as a "bucket", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. It's a required field. |
    -| `whenUnsatisfiable` | string | WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,   but giving higher precedence to topologies that would help reduce the   skew. A constraint is considered "Unsatisfiable" for an incoming pod if and only if every possible node assignment for that pod would violate "MaxSkew" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P |   P   |   P   | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field. |
    -
     ### Cluster Condition
     
     | Name | Type | Description |
    @@ -361,14 +336,14 @@ TopologySpreadConstraint specifies how to spread matching pods among the given t
     
     | Name | Type | Description |
     | --- | --- | --- |
    -| `metadata` | [Object Meta](k8s1.13.5.md#object-meta) | The PersistentVolume metadata. Must include the `name` field. Required. |
    +| `metadata` | [Object Meta](k8s1.28.2.md#object-meta) | The PersistentVolume metadata. Must include the `name` field. Required. |
     | `spec` | [Persistent Volume Spec](#persistent-volume-spec) | The specification of a persistent volume for `Domain on PV` domain. Required. This section provides a subset of fields in standard Kubernetes PersistentVolume specifications. |
     
     ### Persistent Volume Claim
     
     | Name | Type | Description |
     | --- | --- | --- |
    -| `metadata` | [Object Meta](k8s1.13.5.md#object-meta) | The PersistentVolumeClaim metadata. Must include the `name` field. Required. |
    +| `metadata` | [Object Meta](k8s1.28.2.md#object-meta) | The PersistentVolumeClaim metadata. Must include the `name` field. Required. |
     | `spec` | [Persistent Volume Claim Spec](#persistent-volume-claim-spec) | The specifications of a persistent volume claim for `Domain on PV` domain. Required. This section provides a subset of fields in standard Kubernetes PersistentVolumeClaim specifications. |
     
     ### Auxiliary Image
    @@ -410,8 +385,8 @@ TopologySpreadConstraint specifies how to spread matching pods among the given t
     | Name | Type | Description |
     | --- | --- | --- |
     | `capacity` | Map | Capacity is the description of the persistent volume's resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity |
    -| `hostPath` | [Host Path Volume Source](k8s1.13.5.md#host-path-volume-source) | HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath. Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling. |
    -| `nfs` | [NFS Volume Source](k8s1.13.5.md#nfs-volume-source) | nfs represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs. Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling. |
    +| `hostPath` | [Host Path Volume Source](k8s1.28.2.md#host-path-volume-source) | HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath. Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling. |
    +| `nfs` | [NFS Volume Source](k8s1.28.2.md#nfs-volume-source) | nfs represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs. Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling. |
     | `persistentVolumeReclaimPolicy` | string | PersistentVolumeReclaimPolicy defines what happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming |
     | `storageClassName` | string | StorageClassName is the name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass. |
     
    @@ -419,7 +394,7 @@ TopologySpreadConstraint specifies how to spread matching pods among the given t
     
     | Name | Type | Description |
     | --- | --- | --- |
    -| `resources` | [Resource Requirements](k8s1.13.5.md#resource-requirements) | Resources represents the minimum resources the volume should have. More info https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources. ResourceRequirements describes the compute resource requirements. |
    +| `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Resources represents the minimum resources the volume should have. More info https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources. ResourceRequirements describes the compute resource requirements. |
     | `storageClassName` | string | StorageClassName is the name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass. |
     | `volumeName` | string | VolumeName is the binding reference to the PersistentVolume backing this claim. |
     
    diff --git a/documentation/domains/k8s1.13.5.md b/documentation/domains/k8s1.13.5.md
    index 58fd0496651..b81a3b673a1 100644
    --- a/documentation/domains/k8s1.13.5.md
    +++ b/documentation/domains/k8s1.13.5.md
    @@ -109,6 +109,21 @@ HostAlias holds the mapping between IP and hostnames that will be injected as an
     | `hostnames` | Array of string | Hostnames for the above IP address. |
     | `ip` | string | IP address of the host file entry. |
     
    +### Probe
    +
    +Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.
    +
    +| Name | Type | Description |
    +| --- | --- | --- |
    +| `exec` | [Exec Action](#exec-action) | One and only one of the following should be specified. Exec specifies the action to take. |
    +| `failureThreshold` | integer | Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. |
    +| `httpGet` | [HTTP Get Action](#http-get-action) | HTTPGet specifies the http request to perform. |
    +| `initialDelaySeconds` | integer | Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes |
    +| `periodSeconds` | integer | How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. |
    +| `successThreshold` | integer | Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1. |
    +| `tcpSocket` | [TCP Socket Action](#tcp-socket-action) | TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported |
    +| `timeoutSeconds` | integer | Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes |
    +
     ### Pod Security Context
     
     PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext.  Field values of container.securityContext take precedence over field values of PodSecurityContext.
    @@ -308,6 +323,21 @@ A label selector is a label query over a set of resources. The result of matchLa
     | Name | Type | Description |
     | --- | --- | --- |
     
    +### Exec Action
    +
    +| Name | Type | Description |
    +| --- | --- | --- |
    +
    +### HTTP Get Action
    +
    +| Name | Type | Description |
    +| --- | --- | --- |
    +
    +### TCP Socket Action
    +
    +| Name | Type | Description |
    +| --- | --- | --- |
    +
     ### Sysctl
     
     | Name | Type | Description |
    diff --git a/documentation/domains/k8s1.28.2.md b/documentation/domains/k8s1.28.2.md
    new file mode 100644
    index 00000000000..c45752edb26
    --- /dev/null
    +++ b/documentation/domains/k8s1.28.2.md
    @@ -0,0 +1,538 @@
    +### Kubernetes   Objects
    +
    +| Name | Type | Description |
    +| --- | --- | --- |
    +
    +### Object Meta
    +
    +ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.
    +
    +| Name | Type | Description |
    +| --- | --- | --- |
    +| `annotations` | object | Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations |
    +| `creationTimestamp` | [Time](#time) | CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.

    Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata | +| `deletionGracePeriodSeconds` | integer | Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only. | +| `deletionTimestamp` | [Time](#time) | DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.

    Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata | +| `finalizers` | Array of string | Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list. | +| `generateName` | string | GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.

    If this field is specified and the generated name exists, the server will return a 409.

    Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency | +| `generation` | integer | A sequence number representing a specific generation of the desired state. Populated by the system. Read-only. | +| `labels` | object | Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels | +| `managedFields` | Array of [Managed Fields Entry](#managed-fields-entry) | ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object. | +| `name` | string | Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names | +| `namespace` | string | Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.

    Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces | +| `ownerReferences` | Array of [Owner Reference](#owner-reference) | List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller. | +| `resourceVersion` | string | An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.

    Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency | +| `selfLink` | string | Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. | +| `uid` | string | UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.

    Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids | + +### Affinity + +Affinity is a group of affinity scheduling rules. + +| Name | Type | Description | +| --- | --- | --- | +| `nodeAffinity` | [Node Affinity](#node-affinity) | Describes node affinity scheduling rules for the pod. | +| `podAffinity` | [Pod Affinity](#pod-affinity) | Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). | +| `podAntiAffinity` | [Pod Anti Affinity](#pod-anti-affinity) | Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). | + +### Container + +A single application container that you want to run within a pod. + +| Name | Type | Description | +| --- | --- | --- | +| `args` | Array of string | Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell | +| `command` | Array of string | Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell | +| `env` | Array of [Env Var](#env-var) | List of environment variables to set in the container. Cannot be updated. | +| `envFrom` | Array of [Env From Source](#env-from-source) | List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. | +| `image` | string | Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets. | +| `imagePullPolicy` | string | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | +| `lifecycle` | [Lifecycle](#lifecycle) | Actions that the management system should take in response to container lifecycle events. Cannot be updated. | +| `livenessProbe` | [Probe](#probe) | Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes | +| `name` | string | Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated. | +| `ports` | Array of [Container Port](#container-port) | List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.0" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated. | +| `readinessProbe` | [Probe](#probe) | Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes | +| `resizePolicy` | Array of [Container Resize Policy](#container-resize-policy) | Resources resize policy for the container. | +| `resources` | [Resource Requirements](#resource-requirements) | Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | +| `restartPolicy` | string | RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is "Always". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as "Always" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" will be shut down. This lifecycle differs from normal init containers and is often referred to as a "sidecar" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed. | +| `securityContext` | [Security Context](#security-context) | SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ | +| `startupProbe` | [Probe](#probe) | StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes | +| `stdin` | Boolean | Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false. | +| `stdinOnce` | Boolean | Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false | +| `terminationMessagePath` | string | Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated. | +| `terminationMessagePolicy` | string | Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated. | +| `tty` | Boolean | Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false. | +| `volumeDevices` | Array of [Volume Device](#volume-device) | volumeDevices is the list of block devices to be used by the container. | +| `volumeMounts` | Array of [Volume Mount](#volume-mount) | Pod volumes to mount into the container's filesystem. Cannot be updated. | +| `workingDir` | string | Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated. | + +### Security Context + +SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence. + +| Name | Type | Description | +| --- | --- | --- | +| `allowPrivilegeEscalation` | Boolean | AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows. | +| `capabilities` | [Capabilities](#capabilities) | The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. | +| `privileged` | Boolean | Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows. | +| `procMount` | string | procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. | +| `readOnlyRootFilesystem` | Boolean | Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows. | +| `runAsGroup` | integer | The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. | +| `runAsNonRoot` | Boolean | Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. | +| `runAsUser` | integer | The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. | +| `seccompProfile` | [Seccomp Profile](#seccomp-profile) | The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows. | +| `seLinuxOptions` | [SE Linux Options](#se-linux-options) | The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. | +| `windowsOptions` | [Windows Security Context Options](#windows-security-context-options) | The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. | + +### Env Var + +EnvVar represents an environment variable present in a Container. + +| Name | Type | Description | +| --- | --- | --- | +| `name` | string | Name of the environment variable. Must be a C_IDENTIFIER. | +| `value` | string | Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "". | +| `valueFrom` | [Env Var Source](#env-var-source) | Source for the environment variable's value. Cannot be used if value is not empty. | + +### Env From Source + +EnvFromSource represents the source of a set of ConfigMaps + +| Name | Type | Description | +| --- | --- | --- | +| `configMapRef` | [Config Map Env Source](#config-map-env-source) | The ConfigMap to select from | +| `prefix` | string | An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. | +| `secretRef` | [Secret Env Source](#secret-env-source) | The Secret to select from | + +### Host Alias + +HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file. + +| Name | Type | Description | +| --- | --- | --- | +| `hostnames` | Array of string | Hostnames for the above IP address. | +| `ip` | string | IP address of the host file entry. | + +### Probe + +Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic. + +| Name | Type | Description | +| --- | --- | --- | +| `exec` | [Exec Action](#exec-action) | Exec specifies the action to take. | +| `failureThreshold` | integer | Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. | +| `grpc` | [GRPC Action](#grpc-action) | GRPC specifies an action involving a GRPC port. | +| `httpGet` | [HTTP Get Action](#http-get-action) | HTTPGet specifies the http request to perform. | +| `initialDelaySeconds` | integer | Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes | +| `periodSeconds` | integer | How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. | +| `successThreshold` | integer | Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. | +| `tcpSocket` | [TCP Socket Action](#tcp-socket-action) | TCPSocket specifies an action involving a TCP port. | +| `terminationGracePeriodSeconds` | integer | Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. | +| `timeoutSeconds` | integer | Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes | + +### Pod Security Context + +PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext. + +| Name | Type | Description | +| --- | --- | --- | +| `fsGroup` | integer | A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:

    1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----

    If unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows. | +| `fsGroupChangePolicy` | string | fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. Note that this field cannot be set when spec.os.name is windows. | +| `runAsGroup` | integer | The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. | +| `runAsNonRoot` | Boolean | Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. | +| `runAsUser` | integer | The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. | +| `seccompProfile` | [Seccomp Profile](#seccomp-profile) | The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows. | +| `seLinuxOptions` | [SE Linux Options](#se-linux-options) | The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. | +| `supplementalGroups` | Array of integer | A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for the uid of the container process. If unspecified, no additional groups are added to any container. Note that group memberships defined in the container image for the uid of the container process are still effective, even if they are not included in this list. Note that this field cannot be set when spec.os.name is windows. | +| `sysctls` | Array of [Sysctl](#sysctl) | Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows. | +| `windowsOptions` | [Windows Security Context Options](#windows-security-context-options) | The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. | + +### Pod Readiness Gate + +PodReadinessGate contains the reference to a pod condition + +| Name | Type | Description | +| --- | --- | --- | +| `conditionType` | string | ConditionType refers to a condition in the pod's condition list with matching type. | + +### Resource Requirements + +ResourceRequirements describes the compute resource requirements. + +| Name | Type | Description | +| --- | --- | --- | +| `claims` | Array of [Resource Claim](#resource-claim) | Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.

    This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.

    This field is immutable. It can only be set for containers. | +| `limits` | object | Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | +| `requests` | object | Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ | + +### Toleration + +The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + +| Name | Type | Description | +| --- | --- | --- | +| `effect` | string | Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. | +| `key` | string | Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. | +| `operator` | string | Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. | +| `tolerationSeconds` | integer | TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. | +| `value` | string | Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. | + +### Topology Spread Constraint + +TopologySpreadConstraint specifies how to spread matching pods among the given topology. + +| Name | Type | Description | +| --- | --- | --- | +| `labelSelector` | [Label Selector](#label-selector) | LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain. | +| `matchLabelKeys` | Array of string | MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.

    This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). | +| `maxSkew` | integer | MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed. | +| `minDomains` | integer | MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.

    For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.

    This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default). | +| `nodeAffinityPolicy` | string | NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.

    If this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. | +| `nodeTaintsPolicy` | string | NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.

    If this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. | +| `topologyKey` | string | TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a "bucket", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. It's a required field. | +| `whenUnsatisfiable` | string | WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,
    but giving higher precedence to topologies that would help reduce the
    skew.
    A constraint is considered "Unsatisfiable" for an incoming pod if and only if every possible node assignment for that pod would violate "MaxSkew" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field. | + +### Volume Mount + +VolumeMount describes a mounting of a Volume within a container. + +| Name | Type | Description | +| --- | --- | --- | +| `mountPath` | string | Path within the container at which the volume should be mounted. Must not contain ':'. | +| `mountPropagation` | string | mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. | +| `name` | string | This must match the Name of a Volume. | +| `readOnly` | Boolean | Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. | +| `subPath` | string | Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). | +| `subPathExpr` | string | Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. | + +### Volume + +Volume represents a named volume in a pod that may be accessed by any container in the pod. + +| Name | Type | Description | +| --- | --- | --- | +| `awsElasticBlockStore` | [AWS Elastic Block Store Volume Source](#aws-elastic-block-store-volume-source) | awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore | +| `azureDisk` | [Azure Disk Volume Source](#azure-disk-volume-source) | azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. | +| `azureFile` | [Azure File Volume Source](#azure-file-volume-source) | azureFile represents an Azure File Service mount on the host and bind mount to the pod. | +| `cephfs` | [Ceph FS Volume Source](#ceph-fs-volume-source) | cephFS represents a Ceph FS mount on the host that shares a pod's lifetime | +| `cinder` | [Cinder Volume Source](#cinder-volume-source) | cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md | +| `configMap` | [Config Map Volume Source](#config-map-volume-source) | configMap represents a configMap that should populate this volume | +| `csi` | [CSI Volume Source](#csi-volume-source) | csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature). | +| `downwardAPI` | [Downward API Volume Source](#downward-api-volume-source) | downwardAPI represents downward API about the pod that should populate this volume | +| `emptyDir` | [Empty Dir Volume Source](#empty-dir-volume-source) | emptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir | +| `ephemeral` | [Ephemeral Volume Source](#ephemeral-volume-source) | ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed.

    Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity
    tracking are needed,
    c) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through
    a PersistentVolumeClaim (see EphemeralVolumeSource for more
    information on the connection between this volume type
    and PersistentVolumeClaim).

    Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod.

    Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information.

    A pod can use both types of ephemeral volumes and persistent volumes at the same time. | +| `fc` | [FC Volume Source](#fc-volume-source) | fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod. | +| `flexVolume` | [Flex Volume Source](#flex-volume-source) | flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. | +| `flocker` | [Flocker Volume Source](#flocker-volume-source) | flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running | +| `gcePersistentDisk` | [GCE Persistent Disk Volume Source](#gce-persistent-disk-volume-source) | gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk | +| `gitRepo` | [Git Repo Volume Source](#git-repo-volume-source) | gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container. | +| `glusterfs` | [Glusterfs Volume Source](#glusterfs-volume-source) | glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md | +| `hostPath` | [Host Path Volume Source](#host-path-volume-source) | hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath | +| `iscsi` | [ISCSI Volume Source](#iscsi-volume-source) | iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md | +| `name` | string | name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names | +| `nfs` | [NFS Volume Source](#nfs-volume-source) | nfs represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs | +| `persistentVolumeClaim` | [Persistent Volume Claim Volume Source](#persistent-volume-claim-volume-source) | persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims | +| `photonPersistentDisk` | [Photon Persistent Disk Volume Source](#photon-persistent-disk-volume-source) | photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine | +| `portworxVolume` | [Portworx Volume Source](#portworx-volume-source) | portworxVolume represents a portworx volume attached and mounted on kubelets host machine | +| `projected` | [Projected Volume Source](#projected-volume-source) | projected items for all in one resources secrets, configmaps, and downward API | +| `quobyte` | [Quobyte Volume Source](#quobyte-volume-source) | quobyte represents a Quobyte mount on the host that shares a pod's lifetime | +| `rbd` | [RBD Volume Source](#rbd-volume-source) | rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md | +| `scaleIO` | [Scale IO Volume Source](#scale-io-volume-source) | scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. | +| `secret` | [Secret Volume Source](#secret-volume-source) | secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret | +| `storageos` | [Storage OS Volume Source](#storage-os-volume-source) | storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. | +| `vsphereVolume` | [Vsphere Virtual Disk Volume Source](#vsphere-virtual-disk-volume-source) | vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine | + +### Time + +| Name | Type | Description | +| --- | --- | --- | + +### Managed Fields Entry + +| Name | Type | Description | +| --- | --- | --- | + +### Owner Reference + +| Name | Type | Description | +| --- | --- | --- | + +### Node Affinity + +| Name | Type | Description | +| --- | --- | --- | + +### Pod Affinity + +| Name | Type | Description | +| --- | --- | --- | + +### Pod Anti Affinity + +| Name | Type | Description | +| --- | --- | --- | + +### Env Var + +| Name | Type | Description | +| --- | --- | --- | + +### Env From Source + +| Name | Type | Description | +| --- | --- | --- | + +### Lifecycle + +| Name | Type | Description | +| --- | --- | --- | + +### Probe + +| Name | Type | Description | +| --- | --- | --- | + +### Container Port + +| Name | Type | Description | +| --- | --- | --- | + +### Container Resize Policy + +| Name | Type | Description | +| --- | --- | --- | + +### Resource Requirements + +| Name | Type | Description | +| --- | --- | --- | + +### Security Context + +| Name | Type | Description | +| --- | --- | --- | + +### Volume Device + +| Name | Type | Description | +| --- | --- | --- | + +### Volume Mount + +| Name | Type | Description | +| --- | --- | --- | + +### Capabilities + +| Name | Type | Description | +| --- | --- | --- | + +### Seccomp Profile + +| Name | Type | Description | +| --- | --- | --- | + +### SE Linux Options + +| Name | Type | Description | +| --- | --- | --- | + +### Windows Security Context Options + +| Name | Type | Description | +| --- | --- | --- | + +### Env Var Source + +| Name | Type | Description | +| --- | --- | --- | + +### Config Map Env Source + +| Name | Type | Description | +| --- | --- | --- | + +### Secret Env Source + +| Name | Type | Description | +| --- | --- | --- | + +### Exec Action + +| Name | Type | Description | +| --- | --- | --- | + +### GRPC Action + +| Name | Type | Description | +| --- | --- | --- | + +### HTTP Get Action + +| Name | Type | Description | +| --- | --- | --- | + +### TCP Socket Action + +| Name | Type | Description | +| --- | --- | --- | + +### Sysctl + +| Name | Type | Description | +| --- | --- | --- | + +### Resource Claim + +| Name | Type | Description | +| --- | --- | --- | + +### Label Selector + +| Name | Type | Description | +| --- | --- | --- | + +### AWS Elastic Block Store Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Azure Disk Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Azure File Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Ceph FS Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Cinder Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Config Map Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### CSI Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Downward API Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Empty Dir Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Ephemeral Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### FC Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Flex Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Flocker Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### GCE Persistent Disk Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Git Repo Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Glusterfs Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Host Path Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### ISCSI Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### NFS Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Persistent Volume Claim Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Photon Persistent Disk Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Portworx Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Projected Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Quobyte Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### RBD Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Scale IO Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Secret Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Storage OS Volume Source + +| Name | Type | Description | +| --- | --- | --- | + +### Vsphere Virtual Disk Volume Source + +| Name | Type | Description | +| --- | --- | --- | \ No newline at end of file diff --git a/json-schema-generator/src/main/java/oracle/kubernetes/json/KubernetesSchemaReference.java b/json-schema-generator/src/main/java/oracle/kubernetes/json/KubernetesSchemaReference.java index 67767d59b31..1958185513a 100644 --- a/json-schema-generator/src/main/java/oracle/kubernetes/json/KubernetesSchemaReference.java +++ b/json-schema-generator/src/main/java/oracle/kubernetes/json/KubernetesSchemaReference.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. +// Copyright (c) 2019, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.json; @@ -10,7 +10,7 @@ public class KubernetesSchemaReference { private static final String K8S_SCHEMA_URL = - "https://github.com/garethr/kubernetes-json-schema/blob/master/v%s/_definitions.json"; + "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v%s/_definitions.json"; private static final String K8S_SCHEMA_CACHE = "caches/kubernetes-%s.json"; private static final String K8S_MARKDOWN_LINK = "k8s%s.md"; diff --git a/json-schema-generator/src/main/resources/oracle/kubernetes/json/caches/kubernetes-1.28.2.json b/json-schema-generator/src/main/resources/oracle/kubernetes/json/caches/kubernetes-1.28.2.json new file mode 100644 index 00000000000..6d7e6aa8585 --- /dev/null +++ b/json-schema-generator/src/main/resources/oracle/kubernetes/json/caches/kubernetes-1.28.2.json @@ -0,0 +1,17775 @@ +{ + "definitions": { + "io.k8s.api.admissionregistration.v1.MatchCondition": { + "description": "MatchCondition represents a condition which must by fulfilled for a request to be sent to a webhook.", + "properties": { + "expression": { + "description": "Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:\n\n'object' - The object from the incoming request. The value is null for DELETE requests. 'oldObject' - The existing object. The value is null for CREATE requests. 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\nDocumentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/\n\nRequired.", + "type": "string" + }, + "name": { + "description": "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes. A good name should be descriptive of the associated expression. Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')\n\nRequired.", + "type": "string" + } + }, + "required": [ + "name", + "expression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1.MutatingWebhook": { + "description": "MutatingWebhook describes an admission webhook and the resources and operations it applies to.", + "properties": { + "admissionReviewVersions": { + "description": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy.", + "items": { + "type": "string" + }, + "type": "array" + }, + "clientConfig": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.WebhookClientConfig", + "description": "ClientConfig defines how to communicate with the hook. Required" + }, + "failurePolicy": { + "description": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Fail.", + "type": "string" + }, + "matchConditions": { + "description": "MatchConditions is a list of conditions that must be met for a request to be sent to this webhook. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the webhook is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the webhook is called.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the error is ignored and the webhook is skipped\n\nThis is a beta feature and managed by the AdmissionWebhookMatchConditions feature gate.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.MatchCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "matchPolicy": { + "description": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Equivalent\"", + "type": "string" + }, + "name": { + "description": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "type": "string" + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "objectSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "ObjectSelector decides whether to run the webhook based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the webhook, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything." + }, + "reinvocationPolicy": { + "description": "reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. Allowed values are \"Never\" and \"IfNeeded\".\n\nNever: the webhook will not be called more than once in a single admission evaluation.\n\nIfNeeded: the webhook will be called at least one additional time as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call. Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted. Note: * the number of additional invocations is not guaranteed to be exactly one. * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. * webhooks that use this option may be reordered to minimize the number of additional invocations. * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead.\n\nDefaults to \"Never\".", + "type": "string" + }, + "rules": { + "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.RuleWithOperations" + }, + "type": "array" + }, + "sideEffects": { + "description": "SideEffects states whether this webhook has side effects. Acceptable values are: None, NoneOnDryRun (webhooks created via v1beta1 may also specify Some or Unknown). Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission chain and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some.", + "type": "string" + }, + "timeoutSeconds": { + "description": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 10 seconds.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "name", + "clientConfig", + "sideEffects", + "admissionReviewVersions" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1.MutatingWebhookConfiguration": { + "description": "MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "MutatingWebhookConfiguration" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "webhooks": { + "description": "Webhooks is a list of webhooks and the affected resources and operations.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.MutatingWebhook" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "MutatingWebhookConfiguration", + "version": "v1" + } + ] + }, + "io.k8s.api.admissionregistration.v1.MutatingWebhookConfigurationList": { + "description": "MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of MutatingWebhookConfiguration.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.MutatingWebhookConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "MutatingWebhookConfigurationList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "MutatingWebhookConfigurationList", + "version": "v1" + } + ] + }, + "io.k8s.api.admissionregistration.v1.RuleWithOperations": { + "description": "RuleWithOperations is a tuple of Operations and Resources. It is recommended to make sure that all the tuple expansions are valid.", + "properties": { + "apiGroups": { + "description": "APIGroups is the API groups the resources belong to. '*' is all groups. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "apiVersions": { + "description": "APIVersions is the API versions the resources belong to. '*' is all versions. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "operations": { + "description": "Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "scope": { + "description": "scope specifies the scope of this rule. Valid values are \"Cluster\", \"Namespaced\", and \"*\" \"Cluster\" means that only cluster-scoped resources will match this rule. Namespace API objects are cluster-scoped. \"Namespaced\" means that only namespaced resources will match this rule. \"*\" means that there are no scope restrictions. Subresources match the scope of their parent resource. Default is \"*\".", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "`name` is the name of the service. Required", + "type": "string" + }, + "namespace": { + "description": "`namespace` is the namespace of the service. Required", + "type": "string" + }, + "path": { + "description": "`path` is an optional URL path which will be sent in any request to this service.", + "type": "string" + }, + "port": { + "description": "If specified, the port on the service that hosting webhook. Default to 443 for backward compatibility. `port` should be a valid port number (1-65535, inclusive).", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1.ValidatingWebhook": { + "description": "ValidatingWebhook describes an admission webhook and the resources and operations it applies to.", + "properties": { + "admissionReviewVersions": { + "description": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy.", + "items": { + "type": "string" + }, + "type": "array" + }, + "clientConfig": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.WebhookClientConfig", + "description": "ClientConfig defines how to communicate with the hook. Required" + }, + "failurePolicy": { + "description": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Fail.", + "type": "string" + }, + "matchConditions": { + "description": "MatchConditions is a list of conditions that must be met for a request to be sent to this webhook. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the webhook is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the webhook is called.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the error is ignored and the webhook is skipped\n\nThis is a beta feature and managed by the AdmissionWebhookMatchConditions feature gate.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.MatchCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "matchPolicy": { + "description": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Equivalent\"", + "type": "string" + }, + "name": { + "description": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "type": "string" + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "objectSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "ObjectSelector decides whether to run the webhook based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the webhook, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything." + }, + "rules": { + "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.RuleWithOperations" + }, + "type": "array" + }, + "sideEffects": { + "description": "SideEffects states whether this webhook has side effects. Acceptable values are: None, NoneOnDryRun (webhooks created via v1beta1 may also specify Some or Unknown). Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission chain and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some.", + "type": "string" + }, + "timeoutSeconds": { + "description": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 10 seconds.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "name", + "clientConfig", + "sideEffects", + "admissionReviewVersions" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1.ValidatingWebhookConfiguration": { + "description": "ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingWebhookConfiguration" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "webhooks": { + "description": "Webhooks is a list of webhooks and the affected resources and operations.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.ValidatingWebhook" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingWebhookConfiguration", + "version": "v1" + } + ] + }, + "io.k8s.api.admissionregistration.v1.ValidatingWebhookConfigurationList": { + "description": "ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ValidatingWebhookConfiguration.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.ValidatingWebhookConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingWebhookConfigurationList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingWebhookConfigurationList", + "version": "v1" + } + ] + }, + "io.k8s.api.admissionregistration.v1.WebhookClientConfig": { + "description": "WebhookClientConfig contains the information to make a TLS connection with the webhook", + "properties": { + "caBundle": { + "description": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "service": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1.ServiceReference", + "description": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`." + }, + "url": { + "description": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.AuditAnnotation": { + "description": "AuditAnnotation describes how to produce an audit annotation for an API request.", + "properties": { + "key": { + "description": "key specifies the audit annotation key. The audit annotation keys of a ValidatingAdmissionPolicy must be unique. The key must be a qualified name ([A-Za-z0-9][-A-Za-z0-9_.]*) no more than 63 bytes in length.\n\nThe key is combined with the resource name of the ValidatingAdmissionPolicy to construct an audit annotation key: \"{ValidatingAdmissionPolicy name}/{key}\".\n\nIf an admission webhook uses the same resource name as this ValidatingAdmissionPolicy and the same audit annotation key, the annotation key will be identical. In this case, the first annotation written with the key will be included in the audit event and all subsequent annotations with the same key will be discarded.\n\nRequired.", + "type": "string" + }, + "valueExpression": { + "description": "valueExpression represents the expression which is evaluated by CEL to produce an audit annotation value. The expression must evaluate to either a string or null value. If the expression evaluates to a string, the audit annotation is included with the string value. If the expression evaluates to null or empty string the audit annotation will be omitted. The valueExpression may be no longer than 5kb in length. If the result of the valueExpression is more than 10kb in length, it will be truncated to 10kb.\n\nIf multiple ValidatingAdmissionPolicyBinding resources match an API request, then the valueExpression will be evaluated for each binding. All unique values produced by the valueExpressions will be joined together in a comma-separated list.\n\nRequired.", + "type": "string" + } + }, + "required": [ + "key", + "valueExpression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning": { + "description": "ExpressionWarning is a warning information that targets a specific expression.", + "properties": { + "fieldRef": { + "description": "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"", + "type": "string" + }, + "warning": { + "description": "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.", + "type": "string" + } + }, + "required": [ + "fieldRef", + "warning" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.MatchCondition": { + "properties": { + "expression": { + "description": "Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:\n\n'object' - The object from the incoming request. The value is null for DELETE requests. 'oldObject' - The existing object. The value is null for CREATE requests. 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\nDocumentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/\n\nRequired.", + "type": "string" + }, + "name": { + "description": "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes. A good name should be descriptive of the associated expression. Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')\n\nRequired.", + "type": "string" + } + }, + "required": [ + "name", + "expression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.MatchResources": { + "description": "MatchResources decides whether to run the admission control policy on an object based on whether it meets the match criteria. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)", + "properties": { + "excludeResourceRules": { + "description": "ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.NamedRuleWithOperations" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchPolicy": { + "description": "matchPolicy defines how the \"MatchResources\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy.\n\nDefaults to \"Equivalent\"", + "type": "string" + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the admission control policy on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the policy.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the policy on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "objectSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "ObjectSelector decides whether to run the validation based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the cel validation, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything." + }, + "resourceRules": { + "description": "ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. The policy cares about an operation if it matches _any_ Rule.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.NamedRuleWithOperations" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1alpha1.NamedRuleWithOperations": { + "description": "NamedRuleWithOperations is a tuple of Operations and Resources with ResourceNames.", + "properties": { + "apiGroups": { + "description": "APIGroups is the API groups the resources belong to. '*' is all groups. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "apiVersions": { + "description": "APIVersions is the API versions the resources belong to. '*' is all versions. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "operations": { + "description": "Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "scope": { + "description": "scope specifies the scope of this rule. Valid values are \"Cluster\", \"Namespaced\", and \"*\" \"Cluster\" means that only cluster-scoped resources will match this rule. Namespace API objects are cluster-scoped. \"Namespaced\" means that only namespaced resources will match this rule. \"*\" means that there are no scope restrictions. Subresources match the scope of their parent resource. Default is \"*\".", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1alpha1.ParamKind": { + "description": "ParamKind is a tuple of Group Kind and Version.", + "properties": { + "apiVersion": { + "description": "APIVersion is the API group version the resources belong to. In format of \"group/version\". Required.", + "type": "string" + }, + "kind": { + "description": "Kind is the API kind the resources belong to. Required.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1alpha1.ParamRef": { + "description": "ParamRef describes how to locate the params to be used as input to expressions of rules applied by a policy binding.", + "properties": { + "name": { + "description": "`name` is the name of the resource being referenced.\n\n`name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.", + "type": "string" + }, + "namespace": { + "description": "namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both `name` and `selector` fields.\n\nA per-namespace parameter may be used by specifying a namespace-scoped `paramKind` in the policy and leaving this field empty.\n\n- If `paramKind` is cluster-scoped, this field MUST be unset. Setting this field results in a configuration error.\n\n- If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset. Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.", + "type": "string" + }, + "parameterNotFoundAction": { + "description": "`parameterNotFoundAction` controls the behavior of the binding when the resource exists, and name or selector is valid, but there are no parameters matched by the binding. If the value is set to `Allow`, then no matched parameters will be treated as successful validation by the binding. If set to `Deny`, then no matched parameters will be subject to the `failurePolicy` of the policy.\n\nAllowed values are `Allow` or `Deny` Default to `Deny`", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector can be used to match multiple param objects based on their labels. Supply selector: {} to match all resources of the ParamKind.\n\nIf multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset." + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1alpha1.TypeChecking": { + "description": "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy", + "properties": { + "expressionWarnings": { + "description": "The type checking warnings for each expression.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ExpressionWarning" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy": { + "description": "ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicySpec", + "description": "Specification of the desired behavior of the ValidatingAdmissionPolicy." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus", + "description": "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicy", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBinding": { + "description": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.\n\nFor a given admission request, each binding will cause its policy to be evaluated N times, where N is 1 for policies/bindings that don't use params, otherwise N is the number of parameters selected by the binding.\n\nThe CEL expressions of a policy must have a computed CEL cost below the maximum CEL budget. Each evaluation of the policy is given an independent CEL cost budget. Adding/removing policies, bindings, or params can not affect whether a given (policy, binding, param) combination is within its own CEL budget.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicyBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBindingSpec", + "description": "Specification of the desired behavior of the ValidatingAdmissionPolicyBinding." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicyBinding", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBindingList": { + "description": "ValidatingAdmissionPolicyBindingList is a list of ValidatingAdmissionPolicyBinding.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of PolicyBinding.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicyBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicyBindingList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBindingSpec": { + "description": "ValidatingAdmissionPolicyBindingSpec is the specification of the ValidatingAdmissionPolicyBinding.", + "properties": { + "matchResources": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.MatchResources", + "description": "MatchResources declares what resources match this binding and will be validated by it. Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this. If this is unset, all resources matched by the policy are validated by this binding When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated. Note that this is differs from ValidatingAdmissionPolicy matchConstraints, where resourceRules are required." + }, + "paramRef": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ParamRef", + "description": "paramRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param." + }, + "policyName": { + "description": "PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored Required.", + "type": "string" + }, + "validationActions": { + "description": "validationActions declares how Validations of the referenced ValidatingAdmissionPolicy are enforced. If a validation evaluates to false it is always enforced according to these actions.\n\nFailures defined by the ValidatingAdmissionPolicy's FailurePolicy are enforced according to these actions only if the FailurePolicy is set to Fail, otherwise the failures are ignored. This includes compilation errors, runtime errors and misconfigurations of the policy.\n\nvalidationActions is declared as a set of action values. Order does not matter. validationActions may not contain duplicates of the same action.\n\nThe supported actions values are:\n\n\"Deny\" specifies that a validation failure results in a denied request.\n\n\"Warn\" specifies that a validation failure is reported to the request client in HTTP Warning headers, with a warning code of 299. Warnings can be sent both for allowed or denied admission responses.\n\n\"Audit\" specifies that a validation failure is included in the published audit event for the request. The audit event will contain a `validation.policy.admission.k8s.io/validation_failure` audit annotation with a value containing the details of the validation failures, formatted as a JSON list of objects, each with the following fields: - message: The validation failure message string - policy: The resource name of the ValidatingAdmissionPolicy - binding: The resource name of the ValidatingAdmissionPolicyBinding - expressionIndex: The index of the failed validations in the ValidatingAdmissionPolicy - validationActions: The enforcement actions enacted for the validation failure Example audit annotation: `\"validation.policy.admission.k8s.io/validation_failure\": \"[{\"message\": \"Invalid value\", {\"policy\": \"policy.example.com\", {\"binding\": \"policybinding.example.com\", {\"expressionIndex\": \"1\", {\"validationActions\": [\"Audit\"]}]\"`\n\nClients should expect to handle additional values by ignoring any values not recognized.\n\n\"Deny\" and \"Warn\" may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.\n\nRequired.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyList": { + "description": "ValidatingAdmissionPolicyList is a list of ValidatingAdmissionPolicy.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ValidatingAdmissionPolicy.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicyList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicySpec": { + "description": "ValidatingAdmissionPolicySpec is the specification of the desired behavior of the AdmissionPolicy.", + "properties": { + "auditAnnotations": { + "description": "auditAnnotations contains CEL expressions which are used to produce audit annotations for the audit event of the API request. validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is required.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.AuditAnnotation" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "failurePolicy": { + "description": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.", + "type": "string" + }, + "matchConditions": { + "description": "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.MatchCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "matchConstraints": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.MatchResources", + "description": "MatchConstraints specifies what resources this policy is designed to validate. The AdmissionPolicy cares about a request if it matches _all_ Constraints. However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding. Required." + }, + "paramKind": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ParamKind", + "description": "ParamKind specifies the kind of resources used to parameterize this policy. If absent, there are no parameters for this policy and the param CEL variable will not be provided to validation expressions. If ParamKind refers to a non-existent kind, this policy definition is mis-configured and the FailurePolicy is applied. If paramKind is specified but paramRef is unset in ValidatingAdmissionPolicyBinding, the params variable will be null." + }, + "validations": { + "description": "Validations contain CEL expressions which is used to apply the validation. Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is required.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.Validation" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "variables": { + "description": "Variables contain definitions of variables that can be used in composition of other expressions. Each variable is defined as a named CEL expression. The variables defined here will be available under `variables` in other expressions of the policy except MatchConditions because MatchConditions are evaluated before the rest of the policy.\n\nThe expression of a variable can refer to other variables defined earlier in the list but not those after. Thus, Variables must be sorted by the order of first appearance and acyclic.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.Variable" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus": { + "description": "ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.", + "properties": { + "conditions": { + "description": "The conditions represent the latest available observations of a policy's current state.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "observedGeneration": { + "description": "The generation observed by the controller.", + "format": "int64", + "type": "integer" + }, + "typeChecking": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.TypeChecking", + "description": "The results of type checking for each expression. Presence of this field indicates the completion of the type checking." + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.Validation": { + "description": "Validation specifies the CEL expression which is used to apply the validation.", + "properties": { + "expression": { + "description": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'namespaceObject' - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources. - 'variables' - Map of composited variables, from its name to its lazily evaluated value.\n For example, a variable named 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.", + "type": "string" + }, + "message": { + "description": "Message represents the message displayed when validation fails. The message is required if the Expression contains line breaks. The message must not contain line breaks. If unset, the message is \"failed rule: {Rule}\". e.g. \"must be a URL with the host matching spec.host\" If the Expression contains line breaks. Message is required. The message must not contain line breaks. If unset, the message is \"failed Expression: {Expression}\".", + "type": "string" + }, + "messageExpression": { + "description": "messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. Since messageExpression is used as a failure message, it must evaluate to a string. If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. Example: \"object.x must be less than max (\"+string(params.max)+\")\"", + "type": "string" + }, + "reason": { + "description": "Reason represents a machine-readable description of why this validation failed. If this is the first validation in the list to fail, this reason, as well as the corresponding HTTP response code, are used in the HTTP response to the client. The currently supported reasons are: \"Unauthorized\", \"Forbidden\", \"Invalid\", \"RequestEntityTooLarge\". If not set, StatusReasonInvalid is used in the response to the client.", + "type": "string" + } + }, + "required": [ + "expression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1alpha1.Variable": { + "description": "Variable is the definition of a variable that is used for composition.", + "properties": { + "expression": { + "description": "Expression is the expression that will be evaluated as the value of the variable. The CEL expression has access to the same identifiers as the CEL expressions in Validation.", + "type": "string" + }, + "name": { + "description": "Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. The variable can be accessed in other expressions through `variables` For example, if name is \"foo\", the variable will be available as `variables.foo`", + "type": "string" + } + }, + "required": [ + "name", + "expression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.AuditAnnotation": { + "description": "AuditAnnotation describes how to produce an audit annotation for an API request.", + "properties": { + "key": { + "description": "key specifies the audit annotation key. The audit annotation keys of a ValidatingAdmissionPolicy must be unique. The key must be a qualified name ([A-Za-z0-9][-A-Za-z0-9_.]*) no more than 63 bytes in length.\n\nThe key is combined with the resource name of the ValidatingAdmissionPolicy to construct an audit annotation key: \"{ValidatingAdmissionPolicy name}/{key}\".\n\nIf an admission webhook uses the same resource name as this ValidatingAdmissionPolicy and the same audit annotation key, the annotation key will be identical. In this case, the first annotation written with the key will be included in the audit event and all subsequent annotations with the same key will be discarded.\n\nRequired.", + "type": "string" + }, + "valueExpression": { + "description": "valueExpression represents the expression which is evaluated by CEL to produce an audit annotation value. The expression must evaluate to either a string or null value. If the expression evaluates to a string, the audit annotation is included with the string value. If the expression evaluates to null or empty string the audit annotation will be omitted. The valueExpression may be no longer than 5kb in length. If the result of the valueExpression is more than 10kb in length, it will be truncated to 10kb.\n\nIf multiple ValidatingAdmissionPolicyBinding resources match an API request, then the valueExpression will be evaluated for each binding. All unique values produced by the valueExpressions will be joined together in a comma-separated list.\n\nRequired.", + "type": "string" + } + }, + "required": [ + "key", + "valueExpression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.ExpressionWarning": { + "description": "ExpressionWarning is a warning information that targets a specific expression.", + "properties": { + "fieldRef": { + "description": "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"", + "type": "string" + }, + "warning": { + "description": "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.", + "type": "string" + } + }, + "required": [ + "fieldRef", + "warning" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.MatchCondition": { + "description": "MatchCondition represents a condition which must be fulfilled for a request to be sent to a webhook.", + "properties": { + "expression": { + "description": "Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:\n\n'object' - The object from the incoming request. The value is null for DELETE requests. 'oldObject' - The existing object. The value is null for CREATE requests. 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\nDocumentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/\n\nRequired.", + "type": "string" + }, + "name": { + "description": "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes. A good name should be descriptive of the associated expression. Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')\n\nRequired.", + "type": "string" + } + }, + "required": [ + "name", + "expression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.MatchResources": { + "description": "MatchResources decides whether to run the admission control policy on an object based on whether it meets the match criteria. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)", + "properties": { + "excludeResourceRules": { + "description": "ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.NamedRuleWithOperations" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchPolicy": { + "description": "matchPolicy defines how the \"MatchResources\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy.\n\nDefaults to \"Equivalent\"", + "type": "string" + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the admission control policy on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the policy.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the policy on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "objectSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "ObjectSelector decides whether to run the validation based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the cel validation, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything." + }, + "resourceRules": { + "description": "ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. The policy cares about an operation if it matches _any_ Rule.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.NamedRuleWithOperations" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1beta1.NamedRuleWithOperations": { + "description": "NamedRuleWithOperations is a tuple of Operations and Resources with ResourceNames.", + "properties": { + "apiGroups": { + "description": "APIGroups is the API groups the resources belong to. '*' is all groups. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "apiVersions": { + "description": "APIVersions is the API versions the resources belong to. '*' is all versions. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "operations": { + "description": "Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added. If '*' is present, the length of the slice must be one. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "scope": { + "description": "scope specifies the scope of this rule. Valid values are \"Cluster\", \"Namespaced\", and \"*\" \"Cluster\" means that only cluster-scoped resources will match this rule. Namespace API objects are cluster-scoped. \"Namespaced\" means that only namespaced resources will match this rule. \"*\" means that there are no scope restrictions. Subresources match the scope of their parent resource. Default is \"*\".", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1beta1.ParamKind": { + "description": "ParamKind is a tuple of Group Kind and Version.", + "properties": { + "apiVersion": { + "description": "APIVersion is the API group version the resources belong to. In format of \"group/version\". Required.", + "type": "string" + }, + "kind": { + "description": "Kind is the API kind the resources belong to. Required.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1beta1.ParamRef": { + "description": "ParamRef describes how to locate the params to be used as input to expressions of rules applied by a policy binding.", + "properties": { + "name": { + "description": "name is the name of the resource being referenced.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.\n\nA single parameter used for all admission requests can be configured by setting the `name` field, leaving `selector` blank, and setting namespace if `paramKind` is namespace-scoped.", + "type": "string" + }, + "namespace": { + "description": "namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both `name` and `selector` fields.\n\nA per-namespace parameter may be used by specifying a namespace-scoped `paramKind` in the policy and leaving this field empty.\n\n- If `paramKind` is cluster-scoped, this field MUST be unset. Setting this field results in a configuration error.\n\n- If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset. Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.", + "type": "string" + }, + "parameterNotFoundAction": { + "description": "`parameterNotFoundAction` controls the behavior of the binding when the resource exists, and name or selector is valid, but there are no parameters matched by the binding. If the value is set to `Allow`, then no matched parameters will be treated as successful validation by the binding. If set to `Deny`, then no matched parameters will be subject to the `failurePolicy` of the policy.\n\nAllowed values are `Allow` or `Deny`\n\nRequired", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector can be used to match multiple param objects based on their labels. Supply selector: {} to match all resources of the ParamKind.\n\nIf multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset." + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.admissionregistration.v1beta1.TypeChecking": { + "description": "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy", + "properties": { + "expressionWarnings": { + "description": "The type checking warnings for each expression.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ExpressionWarning" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicy": { + "description": "ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicySpec", + "description": "Specification of the desired behavior of the ValidatingAdmissionPolicy." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyStatus", + "description": "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicy", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyBinding": { + "description": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.\n\nFor a given admission request, each binding will cause its policy to be evaluated N times, where N is 1 for policies/bindings that don't use params, otherwise N is the number of parameters selected by the binding.\n\nThe CEL expressions of a policy must have a computed CEL cost below the maximum CEL budget. Each evaluation of the policy is given an independent CEL cost budget. Adding/removing policies, bindings, or params can not affect whether a given (policy, binding, param) combination is within its own CEL budget.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicyBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyBindingSpec", + "description": "Specification of the desired behavior of the ValidatingAdmissionPolicyBinding." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicyBinding", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyBindingList": { + "description": "ValidatingAdmissionPolicyBindingList is a list of ValidatingAdmissionPolicyBinding.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of PolicyBinding.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicyBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicyBindingList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyBindingSpec": { + "description": "ValidatingAdmissionPolicyBindingSpec is the specification of the ValidatingAdmissionPolicyBinding.", + "properties": { + "matchResources": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.MatchResources", + "description": "MatchResources declares what resources match this binding and will be validated by it. Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this. If this is unset, all resources matched by the policy are validated by this binding When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated. Note that this is differs from ValidatingAdmissionPolicy matchConstraints, where resourceRules are required." + }, + "paramRef": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ParamRef", + "description": "paramRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param." + }, + "policyName": { + "description": "PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored Required.", + "type": "string" + }, + "validationActions": { + "description": "validationActions declares how Validations of the referenced ValidatingAdmissionPolicy are enforced. If a validation evaluates to false it is always enforced according to these actions.\n\nFailures defined by the ValidatingAdmissionPolicy's FailurePolicy are enforced according to these actions only if the FailurePolicy is set to Fail, otherwise the failures are ignored. This includes compilation errors, runtime errors and misconfigurations of the policy.\n\nvalidationActions is declared as a set of action values. Order does not matter. validationActions may not contain duplicates of the same action.\n\nThe supported actions values are:\n\n\"Deny\" specifies that a validation failure results in a denied request.\n\n\"Warn\" specifies that a validation failure is reported to the request client in HTTP Warning headers, with a warning code of 299. Warnings can be sent both for allowed or denied admission responses.\n\n\"Audit\" specifies that a validation failure is included in the published audit event for the request. The audit event will contain a `validation.policy.admission.k8s.io/validation_failure` audit annotation with a value containing the details of the validation failures, formatted as a JSON list of objects, each with the following fields: - message: The validation failure message string - policy: The resource name of the ValidatingAdmissionPolicy - binding: The resource name of the ValidatingAdmissionPolicyBinding - expressionIndex: The index of the failed validations in the ValidatingAdmissionPolicy - validationActions: The enforcement actions enacted for the validation failure Example audit annotation: `\"validation.policy.admission.k8s.io/validation_failure\": \"[{\"message\": \"Invalid value\", {\"policy\": \"policy.example.com\", {\"binding\": \"policybinding.example.com\", {\"expressionIndex\": \"1\", {\"validationActions\": [\"Audit\"]}]\"`\n\nClients should expect to handle additional values by ignoring any values not recognized.\n\n\"Deny\" and \"Warn\" may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.\n\nRequired.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyList": { + "description": "ValidatingAdmissionPolicyList is a list of ValidatingAdmissionPolicy.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ValidatingAdmissionPolicy.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ValidatingAdmissionPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingAdmissionPolicyList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicySpec": { + "description": "ValidatingAdmissionPolicySpec is the specification of the desired behavior of the AdmissionPolicy.", + "properties": { + "auditAnnotations": { + "description": "auditAnnotations contains CEL expressions which are used to produce audit annotations for the audit event of the API request. validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is required.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.AuditAnnotation" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "failurePolicy": { + "description": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.", + "type": "string" + }, + "matchConditions": { + "description": "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.MatchCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "matchConstraints": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.MatchResources", + "description": "MatchConstraints specifies what resources this policy is designed to validate. The AdmissionPolicy cares about a request if it matches _all_ Constraints. However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding. Required." + }, + "paramKind": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ParamKind", + "description": "ParamKind specifies the kind of resources used to parameterize this policy. If absent, there are no parameters for this policy and the param CEL variable will not be provided to validation expressions. If ParamKind refers to a non-existent kind, this policy definition is mis-configured and the FailurePolicy is applied. If paramKind is specified but paramRef is unset in ValidatingAdmissionPolicyBinding, the params variable will be null." + }, + "validations": { + "description": "Validations contain CEL expressions which is used to apply the validation. Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is required.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.Validation" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "variables": { + "description": "Variables contain definitions of variables that can be used in composition of other expressions. Each variable is defined as a named CEL expression. The variables defined here will be available under `variables` in other expressions of the policy except MatchConditions because MatchConditions are evaluated before the rest of the policy.\n\nThe expression of a variable can refer to other variables defined earlier in the list but not those after. Thus, Variables must be sorted by the order of first appearance and acyclic.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.Variable" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingAdmissionPolicyStatus": { + "description": "ValidatingAdmissionPolicyStatus represents the status of an admission validation policy.", + "properties": { + "conditions": { + "description": "The conditions represent the latest available observations of a policy's current state.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "observedGeneration": { + "description": "The generation observed by the controller.", + "format": "int64", + "type": "integer" + }, + "typeChecking": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.TypeChecking", + "description": "The results of type checking for each expression. Presence of this field indicates the completion of the type checking." + } + }, + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.Validation": { + "description": "Validation specifies the CEL expression which is used to apply the validation.", + "properties": { + "expression": { + "description": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'namespaceObject' - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources. - 'variables' - Map of composited variables, from its name to its lazily evaluated value.\n For example, a variable named 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.", + "type": "string" + }, + "message": { + "description": "Message represents the message displayed when validation fails. The message is required if the Expression contains line breaks. The message must not contain line breaks. If unset, the message is \"failed rule: {Rule}\". e.g. \"must be a URL with the host matching spec.host\" If the Expression contains line breaks. Message is required. The message must not contain line breaks. If unset, the message is \"failed Expression: {Expression}\".", + "type": "string" + }, + "messageExpression": { + "description": "messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. Since messageExpression is used as a failure message, it must evaluate to a string. If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. Example: \"object.x must be less than max (\"+string(params.max)+\")\"", + "type": "string" + }, + "reason": { + "description": "Reason represents a machine-readable description of why this validation failed. If this is the first validation in the list to fail, this reason, as well as the corresponding HTTP response code, are used in the HTTP response to the client. The currently supported reasons are: \"Unauthorized\", \"Forbidden\", \"Invalid\", \"RequestEntityTooLarge\". If not set, StatusReasonInvalid is used in the response to the client.", + "type": "string" + } + }, + "required": [ + "expression" + ], + "type": "object" + }, + "io.k8s.api.admissionregistration.v1beta1.Variable": { + "description": "Variable is the definition of a variable that is used for composition. A variable is defined as a named expression.", + "properties": { + "expression": { + "description": "Expression is the expression that will be evaluated as the value of the variable. The CEL expression has access to the same identifiers as the CEL expressions in Validation.", + "type": "string" + }, + "name": { + "description": "Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. The variable can be accessed in other expressions through `variables` For example, if name is \"foo\", the variable will be available as `variables.foo`", + "type": "string" + } + }, + "required": [ + "name", + "expression" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.apiserverinternal.v1alpha1.ServerStorageVersion": { + "description": "An API server instance reports the version it can decode and the version it encodes objects to when persisting objects in the backend.", + "properties": { + "apiServerID": { + "description": "The ID of the reporting API server.", + "type": "string" + }, + "decodableVersions": { + "description": "The API server can decode objects encoded in these versions. The encodingVersion must be included in the decodableVersions.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "encodingVersion": { + "description": "The API server encodes the object to this version when persisting it in the backend (e.g., etcd).", + "type": "string" + }, + "servedVersions": { + "description": "The API server can serve these versions. DecodableVersions must include all ServedVersions.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "type": "object" + }, + "io.k8s.api.apiserverinternal.v1alpha1.StorageVersion": { + "description": "Storage version of a specific resource.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageVersion" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "The name is .." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apiserverinternal.v1alpha1.StorageVersionSpec", + "description": "Spec is an empty spec. It is here to comply with Kubernetes API style." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apiserverinternal.v1alpha1.StorageVersionStatus", + "description": "API server instances report the version they can decode and the version they encode objects to when persisting objects in the backend." + } + }, + "required": [ + "spec", + "status" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "internal.apiserver.k8s.io", + "kind": "StorageVersion", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.apiserverinternal.v1alpha1.StorageVersionCondition": { + "description": "Describes the state of the storageVersion at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "observedGeneration": { + "description": "If set, this represents the .metadata.generation that the condition was set based upon.", + "format": "int64", + "type": "integer" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of the condition.", + "type": "string" + } + }, + "required": [ + "type", + "status", + "reason" + ], + "type": "object" + }, + "io.k8s.api.apiserverinternal.v1alpha1.StorageVersionList": { + "description": "A list of StorageVersions.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items holds a list of StorageVersion", + "items": { + "$ref": "#/definitions/io.k8s.api.apiserverinternal.v1alpha1.StorageVersion" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageVersionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "internal.apiserver.k8s.io", + "kind": "StorageVersionList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.apiserverinternal.v1alpha1.StorageVersionSpec": { + "description": "StorageVersionSpec is an empty spec.", + "type": "object" + }, + "io.k8s.api.apiserverinternal.v1alpha1.StorageVersionStatus": { + "description": "API server instances report the versions they can decode and the version they encode objects to when persisting objects in the backend.", + "properties": { + "commonEncodingVersion": { + "description": "If all API server instances agree on the same encoding storage version, then this field is set to that version. Otherwise this field is left empty. API servers should finish updating its storageVersionStatus entry before serving write operations, so that this field will be in sync with the reality.", + "type": "string" + }, + "conditions": { + "description": "The latest available observations of the storageVersion's state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apiserverinternal.v1alpha1.StorageVersionCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "storageVersions": { + "description": "The reported versions per API server instance.", + "items": { + "$ref": "#/definitions/io.k8s.api.apiserverinternal.v1alpha1.ServerStorageVersion" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "apiServerID" + ], + "x-kubernetes-list-type": "map" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.ControllerRevision": { + "description": "ControllerRevision implements an immutable snapshot of state data. Clients are responsible for serializing and deserializing the objects that contain their internal state. Once a ControllerRevision has been successfully created, it can not be updated. The API Server will fail validation of all requests that attempt to mutate the Data field. ControllerRevisions may, however, be deleted. Note that, due to its use by both the DaemonSet and StatefulSet controllers for update and rollback, this object is beta. However, it may be subject to name and representation changes in future releases, and clients should not depend on its stability. It is primarily for internal use by controllers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "data": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension", + "description": "Data is the serialized representation of the state." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevision" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "revision": { + "description": "Revision indicates the revision of the state represented by Data.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "revision" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevision", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.ControllerRevisionList": { + "description": "ControllerRevisionList is a resource containing a list of ControllerRevision objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of ControllerRevisions", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ControllerRevision" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ControllerRevisionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ControllerRevisionList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DaemonSet": { + "description": "DaemonSet represents the configuration of a daemon set.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetSpec", + "description": "The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetStatus", + "description": "The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DaemonSet", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DaemonSetCondition": { + "description": "DaemonSetCondition describes the state of a DaemonSet at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of DaemonSet condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DaemonSetList": { + "description": "DaemonSetList is a collection of daemon sets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "A list of daemon sets.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DaemonSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DaemonSetList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DaemonSetSpec": { + "description": "DaemonSetSpec is the specification of a daemon set.", + "properties": { + "minReadySeconds": { + "description": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). The only allowed template.spec.restartPolicy value is \"Always\". More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetUpdateStrategy", + "description": "An update strategy to replace existing DaemonSet pods with new pods." + } + }, + "required": [ + "selector", + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DaemonSetStatus": { + "description": "DaemonSetStatus represents the current status of a daemon set.", + "properties": { + "collisionCount": { + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a DaemonSet's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DaemonSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentNumberScheduled": { + "description": "The number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "desiredNumberScheduled": { + "description": "The total number of nodes that should be running the daemon pod (including nodes correctly running the daemon pod). More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberAvailable": { + "description": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "numberMisscheduled": { + "description": "The number of nodes that are running the daemon pod, but are not supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", + "format": "int32", + "type": "integer" + }, + "numberReady": { + "description": "numberReady is the number of nodes that should be running the daemon pod and have one or more of the daemon pod running with a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "numberUnavailable": { + "description": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "The most recent generation observed by the daemon set controller.", + "format": "int64", + "type": "integer" + }, + "updatedNumberScheduled": { + "description": "The total number of nodes that are running updated daemon pod", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "currentNumberScheduled", + "numberMisscheduled", + "desiredNumberScheduled", + "numberReady" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DaemonSetUpdateStrategy": { + "description": "DaemonSetUpdateStrategy is a struct used to control the update strategy for a DaemonSet.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1.RollingUpdateDaemonSet", + "description": "Rolling update config params. Present only if type = \"RollingUpdate\"." + }, + "type": { + "description": "Type of daemon set update. Can be \"RollingUpdate\" or \"OnDelete\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.Deployment": { + "description": "Deployment enables declarative updates for Pods and ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Deployment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentSpec", + "description": "Specification of the desired behavior of the Deployment." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStatus", + "description": "Most recently observed status of the Deployment." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "Deployment", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DeploymentCondition": { + "description": "DeploymentCondition describes the state of a deployment at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time this condition was updated." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of deployment condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DeploymentList": { + "description": "DeploymentList is a list of Deployments.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Deployments.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.Deployment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeploymentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "DeploymentList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.DeploymentSpec": { + "description": "DeploymentSpec is the specification of the desired behavior of the Deployment.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "paused": { + "description": "Indicates that the deployment is paused.", + "type": "boolean" + }, + "progressDeadlineSeconds": { + "description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels." + }, + "strategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy", + "description": "The deployment strategy to use to replace existing pods with new ones.", + "x-kubernetes-patch-strategy": "retainKeys" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template describes the pods that will be created. The only allowed template.spec.restartPolicy value is \"Always\"." + } + }, + "required": [ + "selector", + "template" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.DeploymentStatus": { + "description": "DeploymentStatus is the most recently observed status of the Deployment.", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.", + "format": "int32", + "type": "integer" + }, + "collisionCount": { + "description": "Count of hash collisions for the Deployment. The Deployment controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ReplicaSet.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a deployment's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "The generation observed by the deployment controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "readyReplicas is the number of pods targeted by this Deployment with a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).", + "format": "int32", + "type": "integer" + }, + "unavailableReplicas": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of pods that are still required for the deployment to have 100% available capacity. They may either be pods that are running but not yet available or pods that still have not been created.", + "format": "int32", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.DeploymentStrategy": { + "description": "DeploymentStrategy describes how to replace existing pods with new ones.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1.RollingUpdateDeployment", + "description": "Rolling update config params. Present only if DeploymentStrategyType = RollingUpdate." + }, + "type": { + "description": "Type of deployment. Can be \"Recreate\" or \"RollingUpdate\". Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.ReplicaSet": { + "description": "ReplicaSet ensures that a specified number of pod replicas are running at any given time.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSetSpec", + "description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSetStatus", + "description": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ReplicaSet", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.ReplicaSetCondition": { + "description": "ReplicaSetCondition describes the state of a replica set at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of replica set condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.ReplicaSetList": { + "description": "ReplicaSetList is a collection of ReplicaSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ReplicaSets. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicaSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "ReplicaSetList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.ReplicaSetSpec": { + "description": "ReplicaSetSpec is the specification of a ReplicaSet.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Selector is a label query over pods that should match the replica count. Label keys and values that must match in order to be controlled by this replica set. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + } + }, + "required": [ + "selector" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.ReplicaSetStatus": { + "description": "ReplicaSetStatus represents the current status of a ReplicaSet.", + "properties": { + "availableReplicas": { + "description": "The number of available replicas (ready for at least minReadySeconds) for this replica set.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a replica set's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.ReplicaSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "fullyLabeledReplicas": { + "description": "The number of pods that have labels matching the labels of the pod template of the replicaset.", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "readyReplicas is the number of pods targeted by this ReplicaSet with a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the most recently observed number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.RollingUpdateDaemonSet": { + "description": "Spec to control the desired behavior of daemon set rolling update.", + "properties": { + "maxSurge": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of nodes with an existing available DaemonSet pod that can have an updated DaemonSet pod during during an update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up to a minimum of 1. Default value is 0. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their a new pod created before the old pod is marked as deleted. The update starts by launching new pods on 30% of nodes. Once an updated pod is available (Ready for at least minReadySeconds) the old DaemonSet pod on that node is marked deleted. If the old pod becomes unavailable for any reason (Ready transitions to false, is evicted, or is drained) an updated pod is immediatedly created on that node without considering surge limits. Allowing surge implies the possibility that the resources consumed by the daemonset on any given node can double if the readiness check fails, and so resource intensive daemonsets should take into account that they may cause evictions during disruption." + }, + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of DaemonSet pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total number of DaemonSet pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding up. This cannot be 0 if MaxSurge is 0 Default value is 1. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their pods stopped for an update at any given time. The update starts by stopping at most 30% of those DaemonSet pods and then brings up new DaemonSet pods in their place. Once the new pods are available, it then proceeds onto other DaemonSet pods, thus ensuring that at least 70% of original number of DaemonSet pods are available at all times during the update." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.RollingUpdateDeployment": { + "description": "Spec to control the desired behavior of rolling update.", + "properties": { + "maxSurge": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods." + }, + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding down. This can not be 0 if MaxSurge is 0. Defaults to 25%. Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods immediately when the rolling update starts. Once new pods are ready, old ReplicaSet can be scaled down further, followed by scaling up the new ReplicaSet, ensuring that the total number of pods available at all times during the update is at least 70% of desired pods." + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy": { + "description": "RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.", + "properties": { + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding up. This can not be 0. Defaults to 1. This field is alpha-level and is only honored by servers that enable the MaxUnavailableStatefulSet feature. The field applies to all pods in the range 0 to Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it will be counted towards MaxUnavailable." + }, + "partition": { + "description": "Partition indicates the ordinal at which the StatefulSet should be partitioned for updates. During a rolling update, all pods from ordinal Replicas-1 to Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. This is helpful in being able to do a canary based deployment. The default value is 0.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSet": { + "description": "StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\n\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSet" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetSpec", + "description": "Spec defines the desired identities of pods in this set." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetStatus", + "description": "Status is the current status of Pods in this StatefulSet. This data may be out of date by some window of time." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSet", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.StatefulSetCondition": { + "description": "StatefulSetCondition describes the state of a statefulset at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of statefulset condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetList": { + "description": "StatefulSetList is a collection of StatefulSets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of stateful sets.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSet" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StatefulSetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apps", + "kind": "StatefulSetList", + "version": "v1" + } + ] + }, + "io.k8s.api.apps.v1.StatefulSetOrdinals": { + "description": "StatefulSetOrdinals describes the policy used for replica ordinal assignment in this StatefulSet.", + "properties": { + "start": { + "description": "start is the number representing the first replica's index. It may be used to number replicas from an alternate index (eg: 1-indexed) over the default 0-indexed names, or to orchestrate progressive movement of replicas from one StatefulSet to another. If set, replica indices will be in the range:\n [.spec.ordinals.start, .spec.ordinals.start + .spec.replicas).\nIf unset, defaults to 0. Replica indices will be in the range:\n [0, .spec.replicas).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetPersistentVolumeClaimRetentionPolicy": { + "description": "StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs created from the StatefulSet VolumeClaimTemplates.", + "properties": { + "whenDeleted": { + "description": "WhenDeleted specifies what happens to PVCs created from StatefulSet VolumeClaimTemplates when the StatefulSet is deleted. The default policy of `Retain` causes PVCs to not be affected by StatefulSet deletion. The `Delete` policy causes those PVCs to be deleted.", + "type": "string" + }, + "whenScaled": { + "description": "WhenScaled specifies what happens to PVCs created from StatefulSet VolumeClaimTemplates when the StatefulSet is scaled down. The default policy of `Retain` causes PVCs to not be affected by a scaledown. The `Delete` policy causes the associated PVCs for any excess pods above the replica count to be deleted.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetSpec": { + "description": "A StatefulSetSpec is the specification of a StatefulSet.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "ordinals": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetOrdinals", + "description": "ordinals controls the numbering of replica indices in a StatefulSet. The default ordinals behavior assigns a \"0\" index to the first replica and increments the index by one for each additional replica requested. Using the ordinals field requires the StatefulSetStartOrdinal feature gate to be enabled, which is beta." + }, + "persistentVolumeClaimRetentionPolicy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetPersistentVolumeClaimRetentionPolicy", + "description": "persistentVolumeClaimRetentionPolicy describes the lifecycle of persistent volume claims created from volumeClaimTemplates. By default, all persistent volume claims are created as needed and retained until manually deleted. This policy allows the lifecycle to be altered, for example by deleting persistent volume claims when their stateful set is deleted, or when their pod is scaled down. This requires the StatefulSetAutoDeletePVC feature gate to be enabled, which is alpha. +optional" + }, + "podManagementPolicy": { + "description": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", + "type": "string" + }, + "replicas": { + "description": "replicas is the desired number of replicas of the given Template. These are replicas in the sense that they are instantiations of the same Template, but individual replicas also have a consistent identity. If unspecified, defaults to 1.", + "format": "int32", + "type": "integer" + }, + "revisionHistoryLimit": { + "description": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", + "format": "int32", + "type": "integer" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is a label query over pods that should match the replica count. It must match the pod template's labels. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "serviceName": { + "description": "serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set. Pods get DNS/hostnames that follow the pattern: pod-specific-string.serviceName.default.svc.cluster.local where \"pod-specific-string\" is managed by the StatefulSet controller.", + "type": "string" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "template is the object that describes the pod that will be created if insufficient replicas are detected. Each pod stamped out by the StatefulSet will fulfill this Template, but have a unique identity from the rest of the StatefulSet. Each pod will be named with the format -. For example, a pod in a StatefulSet named \"web\" with index number \"3\" would be named \"web-3\". The only allowed template.spec.restartPolicy value is \"Always\"." + }, + "updateStrategy": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetUpdateStrategy", + "description": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template." + }, + "volumeClaimTemplates": { + "description": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaim" + }, + "type": "array" + } + }, + "required": [ + "selector", + "template", + "serviceName" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetStatus": { + "description": "StatefulSetStatus represents the current state of a StatefulSet.", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this statefulset.", + "format": "int32", + "type": "integer" + }, + "collisionCount": { + "description": "collisionCount is the count of hash collisions for the StatefulSet. The StatefulSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a statefulset's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.apps.v1.StatefulSetCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentReplicas": { + "description": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "format": "int32", + "type": "integer" + }, + "currentRevision": { + "description": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "readyReplicas is the number of pods created for this StatefulSet with a Ready Condition.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "replicas is the number of Pods created by the StatefulSet controller.", + "format": "int32", + "type": "integer" + }, + "updateRevision": { + "description": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", + "type": "string" + }, + "updatedReplicas": { + "description": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.apps.v1.StatefulSetUpdateStrategy": { + "description": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "properties": { + "rollingUpdate": { + "$ref": "#/definitions/io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy", + "description": "RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType." + }, + "type": { + "description": "Type indicates the type of the StatefulSetUpdateStrategy. Default is RollingUpdate.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.BoundObjectReference": { + "description": "BoundObjectReference is a reference to an object that a token is bound to.", + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string" + }, + "kind": { + "description": "Kind of the referent. Valid kinds are 'Pod' and 'Secret'.", + "type": "string" + }, + "name": { + "description": "Name of the referent.", + "type": "string" + }, + "uid": { + "description": "UID of the referent.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.SelfSubjectReview": { + "description": "SelfSubjectReview contains the user information that the kube-apiserver has about the user making this request. When using impersonation, users will receive the user info of the user being impersonated. If impersonation or request header authentication is used, any extra keys will have their case ignored and returned as lowercase.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.SelfSubjectReviewStatus", + "description": "Status is filled in by the server with the user attributes." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "SelfSubjectReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authentication.v1.SelfSubjectReviewStatus": { + "description": "SelfSubjectReviewStatus is filled by the kube-apiserver and sent back to a user.", + "properties": { + "userInfo": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.UserInfo", + "description": "User attributes of the user making this request." + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.TokenRequest": { + "description": "TokenRequest requests a token for a given service account.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "TokenRequest" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.TokenRequestSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.TokenRequestStatus", + "description": "Status is filled in by the server and indicates whether the token can be authenticated." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "TokenRequest", + "version": "v1" + } + ] + }, + "io.k8s.api.authentication.v1.TokenRequestSpec": { + "description": "TokenRequestSpec contains client provided parameters of a token request.", + "properties": { + "audiences": { + "description": "Audiences are the intendend audiences of the token. A recipient of a token must identify themself with an identifier in the list of audiences of the token, and otherwise should reject the token. A token issued for multiple audiences may be used to authenticate against any of the audiences listed but implies a high degree of trust between the target audiences.", + "items": { + "type": "string" + }, + "type": "array" + }, + "boundObjectRef": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.BoundObjectReference", + "description": "BoundObjectRef is a reference to an object that the token will be bound to. The token will only be valid for as long as the bound object exists. NOTE: The API server's TokenReview endpoint will validate the BoundObjectRef, but other audiences may not. Keep ExpirationSeconds small if you want prompt revocation." + }, + "expirationSeconds": { + "description": "ExpirationSeconds is the requested duration of validity of the request. The token issuer may return a token with a different validity duration so a client needs to check the 'expiration' field in a response.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "audiences" + ], + "type": "object" + }, + "io.k8s.api.authentication.v1.TokenRequestStatus": { + "description": "TokenRequestStatus is the result of a token request.", + "properties": { + "expirationTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "ExpirationTimestamp is the time of expiration of the returned token." + }, + "token": { + "description": "Token is the opaque bearer token.", + "type": "string" + } + }, + "required": [ + "token", + "expirationTimestamp" + ], + "type": "object" + }, + "io.k8s.api.authentication.v1.TokenReview": { + "description": "TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "TokenReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.TokenReviewSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.TokenReviewStatus", + "description": "Status is filled in by the server and indicates whether the request can be authenticated." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "TokenReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authentication.v1.TokenReviewSpec": { + "description": "TokenReviewSpec is a description of the token authentication request.", + "properties": { + "audiences": { + "description": "Audiences is a list of the identifiers that the resource server presented with the token identifies as. Audience-aware token authenticators will verify that the token was intended for at least one of the audiences in this list. If no audiences are provided, the audience will default to the audience of the Kubernetes apiserver.", + "items": { + "type": "string" + }, + "type": "array" + }, + "token": { + "description": "Token is the opaque bearer token.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.TokenReviewStatus": { + "description": "TokenReviewStatus is the result of the token authentication request.", + "properties": { + "audiences": { + "description": "Audiences are audience identifiers chosen by the authenticator that are compatible with both the TokenReview and token. An identifier is any identifier in the intersection of the TokenReviewSpec audiences and the token's audiences. A client of the TokenReview API that sets the spec.audiences field should validate that a compatible audience identifier is returned in the status.audiences field to ensure that the TokenReview server is audience aware. If a TokenReview returns an empty status.audience field where status.authenticated is \"true\", the token is valid against the audience of the Kubernetes API server.", + "items": { + "type": "string" + }, + "type": "array" + }, + "authenticated": { + "description": "Authenticated indicates that the token was associated with a known user.", + "type": "boolean" + }, + "error": { + "description": "Error indicates that the token couldn't be checked", + "type": "string" + }, + "user": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.UserInfo", + "description": "User is the UserInfo associated with the provided token." + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1.UserInfo": { + "description": "UserInfo holds the information about the user needed to implement the user.Info interface.", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Any additional information provided by the authenticator.", + "type": "object" + }, + "groups": { + "description": "The names of groups this user is a part of.", + "items": { + "type": "string" + }, + "type": "array" + }, + "uid": { + "description": "A unique value that identifies this user across time. If this user is deleted and another user by the same name is added, they will have different UIDs.", + "type": "string" + }, + "username": { + "description": "The name that uniquely identifies this user among all active users.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1alpha1.SelfSubjectReview": { + "description": "SelfSubjectReview contains the user information that the kube-apiserver has about the user making this request. When using impersonation, users will receive the user info of the user being impersonated. If impersonation or request header authentication is used, any extra keys will have their case ignored and returned as lowercase.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1alpha1.SelfSubjectReviewStatus", + "description": "Status is filled in by the server with the user attributes." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "SelfSubjectReview", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.authentication.v1alpha1.SelfSubjectReviewStatus": { + "description": "SelfSubjectReviewStatus is filled by the kube-apiserver and sent back to a user.", + "properties": { + "userInfo": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.UserInfo", + "description": "User attributes of the user making this request." + } + }, + "type": "object" + }, + "io.k8s.api.authentication.v1beta1.SelfSubjectReview": { + "description": "SelfSubjectReview contains the user information that the kube-apiserver has about the user making this request. When using impersonation, users will receive the user info of the user being impersonated. If impersonation or request header authentication is used, any extra keys will have their case ignored and returned as lowercase.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authentication.v1beta1.SelfSubjectReviewStatus", + "description": "Status is filled in by the server with the user attributes." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authentication.k8s.io", + "kind": "SelfSubjectReview", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.authentication.v1beta1.SelfSubjectReviewStatus": { + "description": "SelfSubjectReviewStatus is filled by the kube-apiserver and sent back to a user.", + "properties": { + "userInfo": { + "$ref": "#/definitions/io.k8s.api.authentication.v1.UserInfo", + "description": "User attributes of the user making this request." + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.LocalSubjectAccessReview": { + "description": "LocalSubjectAccessReview checks whether or not a user or group can perform an action in a given namespace. Having a namespace scoped resource makes it much easier to grant namespace scoped policy that includes permissions checking.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LocalSubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated. spec.namespace must be equal to the namespace you made the request against. If empty, it is defaulted." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "LocalSubjectAccessReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.NonResourceAttributes": { + "description": "NonResourceAttributes includes the authorization attributes available for non-resource requests to the Authorizer interface", + "properties": { + "path": { + "description": "Path is the URL path of the request", + "type": "string" + }, + "verb": { + "description": "Verb is the standard HTTP verb", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.NonResourceRule": { + "description": "NonResourceRule holds information that describes a rule for the non-resource", + "properties": { + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verb is a list of kubernetes non-resource API verbs, like: get, post, put, delete, patch, head, options. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1.ResourceAttributes": { + "description": "ResourceAttributes includes the authorization attributes available for resource requests to the Authorizer interface", + "properties": { + "group": { + "description": "Group is the API Group of the Resource. \"*\" means all.", + "type": "string" + }, + "name": { + "description": "Name is the name of the resource being requested for a \"get\" or deleted for a \"delete\". \"\" (empty) means all.", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces \"\" (empty) is defaulted for LocalSubjectAccessReviews \"\" (empty) is empty for cluster-scoped resources \"\" (empty) means \"all\" for namespace scoped resources from a SubjectAccessReview or SelfSubjectAccessReview", + "type": "string" + }, + "resource": { + "description": "Resource is one of the existing resource types. \"*\" means all.", + "type": "string" + }, + "subresource": { + "description": "Subresource is one of the existing resource types. \"\" means none.", + "type": "string" + }, + "verb": { + "description": "Verb is a kubernetes resource API verb, like: get, list, watch, create, update, delete, proxy. \"*\" means all.", + "type": "string" + }, + "version": { + "description": "Version is the API Version of the Resource. \"*\" means all.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.ResourceRule": { + "description": "ResourceRule is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. \"*\" means all in the specified apiGroups.\n \"*/foo\" represents the subresource 'foo' for all resources in the specified apiGroups.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verb is a list of kubernetes resource API verbs, like: get, list, watch, create, update, delete, proxy. \"*\" means all.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1.SelfSubjectAccessReview": { + "description": "SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a spec.namespace means \"in all namespaces\". Self is a special case, because users should always be able to check whether they can perform an action", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated. user and groups must be empty" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SelfSubjectAccessReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec": { + "description": "SelfSubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set", + "properties": { + "nonResourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.NonResourceAttributes", + "description": "NonResourceAttributes describes information for a non-resource access request" + }, + "resourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.ResourceAttributes", + "description": "ResourceAuthorizationAttributes describes information for a resource access request" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.SelfSubjectRulesReview": { + "description": "SelfSubjectRulesReview enumerates the set of actions the current user can perform within a namespace. The returned list of actions may be incomplete depending on the server's authorization mode, and any errors experienced during the evaluation. SelfSubjectRulesReview should be used by UIs to show/hide actions, or to quickly let an end user reason about their permissions. It should NOT Be used by external systems to drive authorization decisions as this raises confused deputy, cache lifetime/revocation, and correctness concerns. SubjectAccessReview, and LocalAccessReview are the correct way to defer authorization decisions to the API server.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SelfSubjectRulesReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec", + "description": "Spec holds information about the request being evaluated." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectRulesReviewStatus", + "description": "Status is filled in by the server and indicates the set of actions a user can perform." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SelfSubjectRulesReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec": { + "description": "SelfSubjectRulesReviewSpec defines the specification for SelfSubjectRulesReview.", + "properties": { + "namespace": { + "description": "Namespace to evaluate rules for. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.SubjectAccessReview": { + "description": "SubjectAccessReview checks whether or not a user or group can perform an action.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SubjectAccessReview" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewSpec", + "description": "Spec holds information about the request being evaluated" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.SubjectAccessReviewStatus", + "description": "Status is filled in by the server and indicates whether the request is allowed or not" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "authorization.k8s.io", + "kind": "SubjectAccessReview", + "version": "v1" + } + ] + }, + "io.k8s.api.authorization.v1.SubjectAccessReviewSpec": { + "description": "SubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set", + "properties": { + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "Extra corresponds to the user.Info.GetExtra() method from the authenticator. Since that is input to the authorizer it needs a reflection here.", + "type": "object" + }, + "groups": { + "description": "Groups is the groups you're testing for.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.NonResourceAttributes", + "description": "NonResourceAttributes describes information for a non-resource access request" + }, + "resourceAttributes": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.ResourceAttributes", + "description": "ResourceAuthorizationAttributes describes information for a resource access request" + }, + "uid": { + "description": "UID information about the requesting user.", + "type": "string" + }, + "user": { + "description": "User is the user you're testing for. If you specify \"User\" but not \"Groups\", then is it interpreted as \"What if User were not a member of any groups", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.authorization.v1.SubjectAccessReviewStatus": { + "description": "SubjectAccessReviewStatus", + "properties": { + "allowed": { + "description": "Allowed is required. True if the action would be allowed, false otherwise.", + "type": "boolean" + }, + "denied": { + "description": "Denied is optional. True if the action would be denied, otherwise false. If both allowed is false and denied is false, then the authorizer has no opinion on whether to authorize the action. Denied may not be true if Allowed is true.", + "type": "boolean" + }, + "evaluationError": { + "description": "EvaluationError is an indication that some error occurred during the authorization check. It is entirely possible to get an error and be able to continue determine authorization status in spite of it. For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request.", + "type": "string" + }, + "reason": { + "description": "Reason is optional. It indicates why a request was allowed or denied.", + "type": "string" + } + }, + "required": [ + "allowed" + ], + "type": "object" + }, + "io.k8s.api.authorization.v1.SubjectRulesReviewStatus": { + "description": "SubjectRulesReviewStatus contains the result of a rules check. This check can be incomplete depending on the set of authorizers the server is configured with and any errors experienced during evaluation. Because authorization rules are additive, if a rule appears in a list it's safe to assume the subject has that permission, even if that list is incomplete.", + "properties": { + "evaluationError": { + "description": "EvaluationError can appear in combination with Rules. It indicates an error occurred during rule evaluation, such as an authorizer that doesn't support rule evaluation, and that ResourceRules and/or NonResourceRules may be incomplete.", + "type": "string" + }, + "incomplete": { + "description": "Incomplete is true when the rules returned by this call are incomplete. This is most commonly encountered when an authorizer, such as an external authorizer, doesn't support rules evaluation.", + "type": "boolean" + }, + "nonResourceRules": { + "description": "NonResourceRules is the list of actions the subject is allowed to perform on non-resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "items": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.NonResourceRule" + }, + "type": "array" + }, + "resourceRules": { + "description": "ResourceRules is the list of actions the subject is allowed to perform on resources. The list ordering isn't significant, may contain duplicates, and possibly be incomplete.", + "items": { + "$ref": "#/definitions/io.k8s.api.authorization.v1.ResourceRule" + }, + "type": "array" + } + }, + "required": [ + "resourceRules", + "nonResourceRules", + "incomplete" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.CrossVersionObjectReference": { + "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "properties": { + "apiVersion": { + "description": "apiVersion is the API version of the referent", + "type": "string" + }, + "kind": { + "description": "kind is the kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "name is the name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler": { + "description": "configuration of a horizontal pod autoscaler.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscaler" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec", + "description": "spec defines the behaviour of autoscaler. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus", + "description": "status is the current information about the autoscaler." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscaler", + "version": "v1" + } + ] + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerList": { + "description": "list of horizontal pod autoscaler objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of horizontal pod autoscaler objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscalerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscalerList", + "version": "v1" + } + ] + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec": { + "description": "specification of a horizontal pod autoscaler.", + "properties": { + "maxReplicas": { + "description": "maxReplicas is the upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas.", + "format": "int32", + "type": "integer" + }, + "minReplicas": { + "description": "minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 pod. minReplicas is allowed to be 0 if the alpha feature gate HPAScaleToZero is enabled and at least one Object or External metric is configured. Scaling is active as long as at least one metric value is available.", + "format": "int32", + "type": "integer" + }, + "scaleTargetRef": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.CrossVersionObjectReference", + "description": "reference to scaled resource; horizontal pod autoscaler will learn the current resource consumption and will set the desired number of pods by using its Scale subresource." + }, + "targetCPUUtilizationPercentage": { + "description": "targetCPUUtilizationPercentage is the target average CPU utilization (represented as a percentage of requested CPU) over all the pods; if not specified the default autoscaling policy will be used.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "scaleTargetRef", + "maxReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus": { + "description": "current status of a horizontal pod autoscaler", + "properties": { + "currentCPUUtilizationPercentage": { + "description": "currentCPUUtilizationPercentage is the current average CPU utilization over all pods, represented as a percentage of requested CPU, e.g. 70 means that an average pod is using now 70% of its requested CPU.", + "format": "int32", + "type": "integer" + }, + "currentReplicas": { + "description": "currentReplicas is the current number of replicas of pods managed by this autoscaler.", + "format": "int32", + "type": "integer" + }, + "desiredReplicas": { + "description": "desiredReplicas is the desired number of replicas of pods managed by this autoscaler.", + "format": "int32", + "type": "integer" + }, + "lastScaleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastScaleTime is the last time the HorizontalPodAutoscaler scaled the number of pods; used by the autoscaler to control how often the number of pods is changed." + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed by this autoscaler.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "currentReplicas", + "desiredReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v1.Scale": { + "description": "Scale represents a scaling request for a resource.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Scale" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.ScaleSpec", + "description": "spec defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v1.ScaleStatus", + "description": "status is the current status of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status. Read-only." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "Scale", + "version": "v1" + } + ] + }, + "io.k8s.api.autoscaling.v1.ScaleSpec": { + "description": "ScaleSpec describes the attributes of a scale subresource.", + "properties": { + "replicas": { + "description": "replicas is the desired number of instances for the scaled object.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.autoscaling.v1.ScaleStatus": { + "description": "ScaleStatus represents the current status of a scale subresource.", + "properties": { + "replicas": { + "description": "replicas is the actual number of observed instances of the scaled object.", + "format": "int32", + "type": "integer" + }, + "selector": { + "description": "selector is the label query over pods that should match the replicas count. This is same as the label selector but in the string format to avoid introspection by clients. The string will be in the same format as the query-param syntax. More info about label selectors: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/", + "type": "string" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ContainerResourceMetricSource": { + "description": "ContainerResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.", + "properties": { + "container": { + "description": "container is the name of the container in the pods of the scaling target", + "type": "string" + }, + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "name", + "target", + "container" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ContainerResourceMetricStatus": { + "description": "ContainerResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.", + "properties": { + "container": { + "description": "container is the name of the container in the pods of the scaling target", + "type": "string" + }, + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + } + }, + "required": [ + "name", + "current", + "container" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.CrossVersionObjectReference": { + "description": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", + "properties": { + "apiVersion": { + "description": "apiVersion is the API version of the referent", + "type": "string" + }, + "kind": { + "description": "kind is the kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "name is the name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ExternalMetricSource": { + "description": "ExternalMetricSource indicates how to scale on a metric not associated with any Kubernetes object (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster).", + "properties": { + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "metric", + "target" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ExternalMetricStatus": { + "description": "ExternalMetricStatus indicates the current value of a global metric not associated with any Kubernetes object.", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + } + }, + "required": [ + "metric", + "current" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.HPAScalingPolicy": { + "description": "HPAScalingPolicy is a single policy which must hold true for a specified past interval.", + "properties": { + "periodSeconds": { + "description": "periodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).", + "format": "int32", + "type": "integer" + }, + "type": { + "description": "type is used to specify the scaling policy.", + "type": "string" + }, + "value": { + "description": "value contains the amount of change which is permitted by the policy. It must be greater than zero", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "type", + "value", + "periodSeconds" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.HPAScalingRules": { + "description": "HPAScalingRules configures the scaling behavior for one direction. These Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.", + "properties": { + "policies": { + "description": "policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HPAScalingPolicy" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "selectPolicy": { + "description": "selectPolicy is used to specify which policy should be used. If not set, the default value Max is used.", + "type": "string" + }, + "stabilizationWindowSeconds": { + "description": "stabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down. StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). If not set, use the default values: - For scale up: 0 (i.e. no stabilization is done). - For scale down: 300 (i.e. the stabilization window is 300 seconds long).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.autoscaling.v2.HorizontalPodAutoscaler": { + "description": "HorizontalPodAutoscaler is the configuration for a horizontal pod autoscaler, which automatically manages the replica count of any resource implementing the scale subresource based on the metrics specified.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscaler" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "metadata is the standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerSpec", + "description": "spec is the specification for the behaviour of the autoscaler. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerStatus", + "description": "status is the current information about the autoscaler." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscaler", + "version": "v2" + } + ] + }, + "io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerBehavior": { + "description": "HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively).", + "properties": { + "scaleDown": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HPAScalingRules", + "description": "scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e., the highest recommendation for the last 300sec is used)." + }, + "scaleUp": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HPAScalingRules", + "description": "scaleUp is scaling policy for scaling Up. If not set, the default value is the higher of:\n * increase no more than 4 pods per 60 seconds\n * double the number of pods per 60 seconds\nNo stabilization is used." + } + }, + "type": "object" + }, + "io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerCondition": { + "description": "HorizontalPodAutoscalerCondition describes the state of a HorizontalPodAutoscaler at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime is the last time the condition transitioned from one status to another" + }, + "message": { + "description": "message is a human-readable explanation containing details about the transition", + "type": "string" + }, + "reason": { + "description": "reason is the reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "status is the status of the condition (True, False, Unknown)", + "type": "string" + }, + "type": { + "description": "type describes the current condition", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerList": { + "description": "HorizontalPodAutoscalerList is a list of horizontal pod autoscaler objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of horizontal pod autoscaler objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HorizontalPodAutoscaler" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "HorizontalPodAutoscalerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "metadata is the standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "autoscaling", + "kind": "HorizontalPodAutoscalerList", + "version": "v2" + } + ] + }, + "io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerSpec": { + "description": "HorizontalPodAutoscalerSpec describes the desired functionality of the HorizontalPodAutoscaler.", + "properties": { + "behavior": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerBehavior", + "description": "behavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). If not set, the default HPAScalingRules for scale up and scale down are used." + }, + "maxReplicas": { + "description": "maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas.", + "format": "int32", + "type": "integer" + }, + "metrics": { + "description": "metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). The desired replica count is calculated multiplying the ratio between the target value and the current value by the current number of pods. Ergo, metrics used must decrease as the pod count is increased, and vice-versa. See the individual metric source types for more information about how each type of metric must respond. If not set, the default metric will be set to 80% average CPU utilization.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricSpec" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "minReplicas": { + "description": "minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 pod. minReplicas is allowed to be 0 if the alpha feature gate HPAScaleToZero is enabled and at least one Object or External metric is configured. Scaling is active as long as at least one metric value is available.", + "format": "int32", + "type": "integer" + }, + "scaleTargetRef": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.CrossVersionObjectReference", + "description": "scaleTargetRef points to the target resource to scale, and is used to the pods for which metrics should be collected, as well as to actually change the replica count." + } + }, + "required": [ + "scaleTargetRef", + "maxReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerStatus": { + "description": "HorizontalPodAutoscalerStatus describes the current status of a horizontal pod autoscaler.", + "properties": { + "conditions": { + "description": "conditions is the set of conditions required for this autoscaler to scale its target, and indicates whether or not those conditions are met.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.HorizontalPodAutoscalerCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentMetrics": { + "description": "currentMetrics is the last read state of the metrics used by this autoscaler.", + "items": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricStatus" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "currentReplicas": { + "description": "currentReplicas is current number of replicas of pods managed by this autoscaler, as last seen by the autoscaler.", + "format": "int32", + "type": "integer" + }, + "desiredReplicas": { + "description": "desiredReplicas is the desired number of replicas of pods managed by this autoscaler, as last calculated by the autoscaler.", + "format": "int32", + "type": "integer" + }, + "lastScaleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastScaleTime is the last time the HorizontalPodAutoscaler scaled the number of pods, used by the autoscaler to control how often the number of pods is changed." + }, + "observedGeneration": { + "description": "observedGeneration is the most recent generation observed by this autoscaler.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "desiredReplicas" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.MetricIdentifier": { + "description": "MetricIdentifier defines the name and optionally selector for a metric", + "properties": { + "name": { + "description": "name is the name of the given metric", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is the string-encoded form of a standard kubernetes label selector for the given metric When set, it is passed as an additional parameter to the metrics server for more specific metrics scoping. When unset, just the metricName will be used to gather metrics." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.MetricSpec": { + "description": "MetricSpec specifies how to scale based on a single metric (only `type` and one other matching field should be set at once).", + "properties": { + "containerResource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ContainerResourceMetricSource", + "description": "containerResource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod of the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. This is an alpha feature and can be enabled by the HPAContainerMetrics feature flag." + }, + "external": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ExternalMetricSource", + "description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)." + }, + "object": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ObjectMetricSource", + "description": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object)." + }, + "pods": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.PodsMetricSource", + "description": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value." + }, + "resource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ResourceMetricSource", + "description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "type": { + "description": "type is the type of metric source. It should be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each mapping to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.MetricStatus": { + "description": "MetricStatus describes the last-read state of a single metric.", + "properties": { + "containerResource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ContainerResourceMetricStatus", + "description": "container resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing a single container in each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "external": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ExternalMetricStatus", + "description": "external refers to a global metric that is not associated with any Kubernetes object. It allows autoscaling based on information coming from components running outside of cluster (for example length of queue in cloud messaging service, or QPS from loadbalancer running outside of cluster)." + }, + "object": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ObjectMetricStatus", + "description": "object refers to a metric describing a single kubernetes object (for example, hits-per-second on an Ingress object)." + }, + "pods": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.PodsMetricStatus", + "description": "pods refers to a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value." + }, + "resource": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.ResourceMetricStatus", + "description": "resource refers to a resource metric (such as those specified in requests and limits) known to Kubernetes describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source." + }, + "type": { + "description": "type is the type of metric source. It will be one of \"ContainerResource\", \"External\", \"Object\", \"Pods\" or \"Resource\", each corresponds to a matching field in the object. Note: \"ContainerResource\" type is available on when the feature-gate HPAContainerMetrics is enabled", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.MetricTarget": { + "description": "MetricTarget defines the target value, average value, or average utilization of a specific metric", + "properties": { + "averageUtilization": { + "description": "averageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods. Currently only valid for Resource metric source type", + "format": "int32", + "type": "integer" + }, + "averageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "averageValue is the target value of the average of the metric across all relevant pods (as a quantity)" + }, + "type": { + "description": "type represents whether the metric type is Utilization, Value, or AverageValue", + "type": "string" + }, + "value": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "value is the target value of the metric (as a quantity)." + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.MetricValueStatus": { + "description": "MetricValueStatus holds the current value for a metric", + "properties": { + "averageUtilization": { + "description": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.", + "format": "int32", + "type": "integer" + }, + "averageValue": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "averageValue is the current value of the average of the metric across all relevant pods (as a quantity)" + }, + "value": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "value is the current value of the metric (as a quantity)." + } + }, + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ObjectMetricSource": { + "description": "ObjectMetricSource indicates how to scale on a metric describing a kubernetes object (for example, hits-per-second on an Ingress object).", + "properties": { + "describedObject": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.CrossVersionObjectReference", + "description": "describedObject specifies the descriptions of a object,such as kind,name apiVersion" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "describedObject", + "target", + "metric" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ObjectMetricStatus": { + "description": "ObjectMetricStatus indicates the current value of a metric describing a kubernetes object (for example, hits-per-second on an Ingress object).", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "describedObject": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.CrossVersionObjectReference", + "description": "DescribedObject specifies the descriptions of a object,such as kind,name apiVersion" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + } + }, + "required": [ + "metric", + "current", + "describedObject" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.PodsMetricSource": { + "description": "PodsMetricSource indicates how to scale on a metric describing each pod in the current scale target (for example, transactions-processed-per-second). The values will be averaged together before being compared to the target value.", + "properties": { + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "metric", + "target" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.PodsMetricStatus": { + "description": "PodsMetricStatus indicates the current value of a metric describing each pod in the current scale target (for example, transactions-processed-per-second).", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "metric": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricIdentifier", + "description": "metric identifies the target metric by name and selector" + } + }, + "required": [ + "metric", + "current" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ResourceMetricSource": { + "description": "ResourceMetricSource indicates how to scale on a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). The values will be averaged together before being compared to the target. Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source. Only one \"target\" type should be set.", + "properties": { + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricTarget", + "description": "target specifies the target value for the given metric" + } + }, + "required": [ + "name", + "target" + ], + "type": "object" + }, + "io.k8s.api.autoscaling.v2.ResourceMetricStatus": { + "description": "ResourceMetricStatus indicates the current value of a resource metric known to Kubernetes, as specified in requests and limits, describing each pod in the current scale target (e.g. CPU or memory). Such metrics are built in to Kubernetes, and have special scaling options on top of those available to normal per-pod metrics using the \"pods\" source.", + "properties": { + "current": { + "$ref": "#/definitions/io.k8s.api.autoscaling.v2.MetricValueStatus", + "description": "current contains the current value for the given metric" + }, + "name": { + "description": "name is the name of the resource in question.", + "type": "string" + } + }, + "required": [ + "name", + "current" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.CronJob": { + "description": "CronJob represents the configuration of a single cron job.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CronJob" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1.CronJobSpec", + "description": "Specification of the desired behavior of a cron job, including the schedule. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.batch.v1.CronJobStatus", + "description": "Current status of a cron job. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "CronJob", + "version": "v1" + } + ] + }, + "io.k8s.api.batch.v1.CronJobList": { + "description": "CronJobList is a collection of cron jobs.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CronJobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.CronJob" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CronJobList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "CronJobList", + "version": "v1" + } + ] + }, + "io.k8s.api.batch.v1.CronJobSpec": { + "description": "CronJobSpec describes how the job execution will look like and when it will actually run.", + "properties": { + "concurrencyPolicy": { + "description": "Specifies how to treat concurrent executions of a Job. Valid values are:\n\n- \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one", + "type": "string" + }, + "failedJobsHistoryLimit": { + "description": "The number of failed finished jobs to retain. Value must be non-negative integer. Defaults to 1.", + "format": "int32", + "type": "integer" + }, + "jobTemplate": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobTemplateSpec", + "description": "Specifies the job that will be created when executing a CronJob." + }, + "schedule": { + "description": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.", + "type": "string" + }, + "startingDeadlineSeconds": { + "description": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", + "format": "int64", + "type": "integer" + }, + "successfulJobsHistoryLimit": { + "description": "The number of successful finished jobs to retain. Value must be non-negative integer. Defaults to 3.", + "format": "int32", + "type": "integer" + }, + "suspend": { + "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", + "type": "boolean" + }, + "timeZone": { + "description": "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones", + "type": "string" + } + }, + "required": [ + "schedule", + "jobTemplate" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.CronJobStatus": { + "description": "CronJobStatus represents the current state of a cron job.", + "properties": { + "active": { + "description": "A list of pointers to currently running jobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "lastScheduleTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Information when was the last time the job was successfully scheduled." + }, + "lastSuccessfulTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Information when was the last time the job successfully completed." + } + }, + "type": "object" + }, + "io.k8s.api.batch.v1.Job": { + "description": "Job represents the configuration of a single job.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Job" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobSpec", + "description": "Specification of the desired behavior of a job. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobStatus", + "description": "Current status of a job. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "Job", + "version": "v1" + } + ] + }, + "io.k8s.api.batch.v1.JobCondition": { + "description": "JobCondition describes current state of a job.", + "properties": { + "lastProbeTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition was checked." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transit from one status to another." + }, + "message": { + "description": "Human readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "(brief) reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of job condition, Complete or Failed.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.JobList": { + "description": "JobList is a collection of jobs.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of Jobs.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.Job" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "JobList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "batch", + "kind": "JobList", + "version": "v1" + } + ] + }, + "io.k8s.api.batch.v1.JobSpec": { + "description": "JobSpec describes how the job execution will look like.", + "properties": { + "activeDeadlineSeconds": { + "description": "Specifies the duration in seconds relative to the startTime that the job may be continuously active before the system tries to terminate it; value must be positive integer. If a Job is suspended (at creation or through an update), this timer will effectively be stopped and reset when the Job is resumed again.", + "format": "int64", + "type": "integer" + }, + "backoffLimit": { + "description": "Specifies the number of retries before marking this job failed. Defaults to 6", + "format": "int32", + "type": "integer" + }, + "backoffLimitPerIndex": { + "description": "Specifies the limit for the number of retries within an index before marking this index as failed. When enabled the number of failures per index is kept in the pod's batch.kubernetes.io/job-index-failure-count annotation. It can only be set when Job's completionMode=Indexed, and the Pod's restart policy is Never. The field is immutable. This field is alpha-level. It can be used when the `JobBackoffLimitPerIndex` feature gate is enabled (disabled by default).", + "format": "int32", + "type": "integer" + }, + "completionMode": { + "description": "completionMode specifies how Pod completions are tracked. It can be `NonIndexed` (default) or `Indexed`.\n\n`NonIndexed` means that the Job is considered complete when there have been .spec.completions successfully completed Pods. Each Pod completion is homologous to each other.\n\n`Indexed` means that the Pods of a Job get an associated completion index from 0 to (.spec.completions - 1), available in the annotation batch.kubernetes.io/job-completion-index. The Job is considered complete when there is one successfully completed Pod for each index. When value is `Indexed`, .spec.completions must be specified and `.spec.parallelism` must be less than or equal to 10^5. In addition, The Pod name takes the form `$(job-name)-$(index)-$(random-string)`, the Pod hostname takes the form `$(job-name)-$(index)`.\n\nMore completion modes can be added in the future. If the Job controller observes a mode that it doesn't recognize, which is possible during upgrades due to version skew, the controller skips updates for the Job.", + "type": "string" + }, + "completions": { + "description": "Specifies the desired number of successfully finished pods the job should be run with. Setting to null means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "format": "int32", + "type": "integer" + }, + "manualSelector": { + "description": "manualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector", + "type": "boolean" + }, + "maxFailedIndexes": { + "description": "Specifies the maximal number of failed indexes before marking the Job as failed, when backoffLimitPerIndex is set. Once the number of failed indexes exceeds this number the entire Job is marked as Failed and its execution is terminated. When left as null the job continues execution of all of its indexes and is marked with the `Complete` Job condition. It can only be specified when backoffLimitPerIndex is set. It can be null or up to completions. It is required and must be less than or equal to 10^4 when is completions greater than 10^5. This field is alpha-level. It can be used when the `JobBackoffLimitPerIndex` feature gate is enabled (disabled by default).", + "format": "int32", + "type": "integer" + }, + "parallelism": { + "description": "Specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "format": "int32", + "type": "integer" + }, + "podFailurePolicy": { + "$ref": "#/definitions/io.k8s.api.batch.v1.PodFailurePolicy", + "description": "Specifies the policy of handling failed pods. In particular, it allows to specify the set of actions and conditions which need to be satisfied to take the associated action. If empty, the default behaviour applies - the counter of failed pods, represented by the jobs's .status.failed field, is incremented and it is checked against the backoffLimit. This field cannot be used in combination with restartPolicy=OnFailure.\n\nThis field is beta-level. It can be used when the `JobPodFailurePolicy` feature gate is enabled (enabled by default)." + }, + "podReplacementPolicy": { + "description": "podReplacementPolicy specifies when to create replacement Pods. Possible values are: - TerminatingOrFailed means that we recreate pods\n when they are terminating (has a metadata.deletionTimestamp) or failed.\n- Failed means to wait until a previously created Pod is fully terminated (has phase\n Failed or Succeeded) before creating a replacement Pod.\n\nWhen using podFailurePolicy, Failed is the the only allowed value. TerminatingOrFailed and Failed are allowed values when podFailurePolicy is not in use. This is an alpha field. Enable JobPodReplacementPolicy to be able to use this field.", + "type": "string" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over pods that should match the pod count. Normally, the system sets this field for you. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors" + }, + "suspend": { + "description": "suspend specifies whether the Job controller should create Pods or not. If a Job is created with suspend set to true, no Pods are created by the Job controller. If a Job is suspended after creation (i.e. the flag goes from false to true), the Job controller will delete all active Pods associated with this Job. Users must design their workload to gracefully handle this. Suspending a Job will reset the StartTime field of the Job, effectively resetting the ActiveDeadlineSeconds timer too. Defaults to false.", + "type": "boolean" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Describes the pod that will be created when executing a job. The only allowed template.spec.restartPolicy values are \"Never\" or \"OnFailure\". More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/" + }, + "ttlSecondsAfterFinished": { + "description": "ttlSecondsAfterFinished limits the lifetime of a Job that has finished execution (either Complete or Failed). If this field is set, ttlSecondsAfterFinished after the Job finishes, it is eligible to be automatically deleted. When the Job is being deleted, its lifecycle guarantees (e.g. finalizers) will be honored. If this field is unset, the Job won't be automatically deleted. If this field is set to zero, the Job becomes eligible to be deleted immediately after it finishes.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "template" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.JobStatus": { + "description": "JobStatus represents the current state of a Job.", + "properties": { + "active": { + "description": "The number of pending and running pods.", + "format": "int32", + "type": "integer" + }, + "completedIndexes": { + "description": "completedIndexes holds the completed indexes when .spec.completionMode = \"Indexed\" in a text format. The indexes are represented as decimal integers separated by commas. The numbers are listed in increasing order. Three or more consecutive numbers are compressed and represented by the first and last element of the series, separated by a hyphen. For example, if the completed indexes are 1, 3, 4, 5 and 7, they are represented as \"1,3-5,7\".", + "type": "string" + }, + "completionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully." + }, + "conditions": { + "description": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobCondition" + }, + "type": "array", + "x-kubernetes-list-type": "atomic", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "failed": { + "description": "The number of pods which reached phase Failed.", + "format": "int32", + "type": "integer" + }, + "failedIndexes": { + "description": "FailedIndexes holds the failed indexes when backoffLimitPerIndex=true. The indexes are represented in the text format analogous as for the `completedIndexes` field, ie. they are kept as decimal integers separated by commas. The numbers are listed in increasing order. Three or more consecutive numbers are compressed and represented by the first and last element of the series, separated by a hyphen. For example, if the failed indexes are 1, 3, 4, 5 and 7, they are represented as \"1,3-5,7\". This field is alpha-level. It can be used when the `JobBackoffLimitPerIndex` feature gate is enabled (disabled by default).", + "type": "string" + }, + "ready": { + "description": "The number of pods which have a Ready condition.\n\nThis field is beta-level. The job controller populates the field when the feature gate JobReadyPods is enabled (enabled by default).", + "format": "int32", + "type": "integer" + }, + "startTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC." + }, + "succeeded": { + "description": "The number of pods which reached phase Succeeded.", + "format": "int32", + "type": "integer" + }, + "terminating": { + "description": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp).\n\nThis field is alpha-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (disabled by default).", + "format": "int32", + "type": "integer" + }, + "uncountedTerminatedPods": { + "$ref": "#/definitions/io.k8s.api.batch.v1.UncountedTerminatedPods", + "description": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null." + } + }, + "type": "object" + }, + "io.k8s.api.batch.v1.JobTemplateSpec": { + "description": "JobTemplateSpec describes the data a Job should have when created from a template", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata of the jobs created from this template. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.batch.v1.JobSpec", + "description": "Specification of the desired behavior of the job. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object" + }, + "io.k8s.api.batch.v1.PodFailurePolicy": { + "description": "PodFailurePolicy describes how failed pods influence the backoffLimit.", + "properties": { + "rules": { + "description": "A list of pod failure policy rules. The rules are evaluated in order. Once a rule matches a Pod failure, the remaining of the rules are ignored. When no rule matches the Pod failure, the default handling applies - the counter of pod failures is incremented and it is checked against the backoffLimit. At most 20 elements are allowed.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.PodFailurePolicyRule" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "rules" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.PodFailurePolicyOnExitCodesRequirement": { + "description": "PodFailurePolicyOnExitCodesRequirement describes the requirement for handling a failed pod based on its container exit codes. In particular, it lookups the .state.terminated.exitCode for each app container and init container status, represented by the .status.containerStatuses and .status.initContainerStatuses fields in the Pod status, respectively. Containers completed with success (exit code 0) are excluded from the requirement check.", + "properties": { + "containerName": { + "description": "Restricts the check for exit codes to the container with the specified name. When null, the rule applies to all containers. When specified, it should match one the container or initContainer names in the pod template.", + "type": "string" + }, + "operator": { + "description": "Represents the relationship between the container exit code(s) and the specified values. Containers completed with success (exit code 0) are excluded from the requirement check. Possible values are:\n\n- In: the requirement is satisfied if at least one container exit code\n (might be multiple if there are multiple containers not restricted\n by the 'containerName' field) is in the set of specified values.\n- NotIn: the requirement is satisfied if at least one container exit code\n (might be multiple if there are multiple containers not restricted\n by the 'containerName' field) is not in the set of specified values.\nAdditional values are considered to be added in the future. Clients should react to an unknown operator by assuming the requirement is not satisfied.", + "type": "string" + }, + "values": { + "description": "Specifies the set of values. Each returned container exit code (might be multiple in case of multiple containers) is checked against this set of values with respect to the operator. The list of values must be ordered and must not contain duplicates. Value '0' cannot be used for the In operator. At least one element is required. At most 255 elements are allowed.", + "items": { + "format": "int32", + "type": "integer" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "required": [ + "operator", + "values" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.PodFailurePolicyOnPodConditionsPattern": { + "description": "PodFailurePolicyOnPodConditionsPattern describes a pattern for matching an actual pod condition type.", + "properties": { + "status": { + "description": "Specifies the required Pod condition status. To match a pod condition it is required that the specified status equals the pod condition status. Defaults to True.", + "type": "string" + }, + "type": { + "description": "Specifies the required Pod condition type. To match a pod condition it is required that specified type equals the pod condition type.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.PodFailurePolicyRule": { + "description": "PodFailurePolicyRule describes how a pod failure is handled when the requirements are met. One of onExitCodes and onPodConditions, but not both, can be used in each rule.", + "properties": { + "action": { + "description": "Specifies the action taken on a pod failure when the requirements are satisfied. Possible values are:\n\n- FailJob: indicates that the pod's job is marked as Failed and all\n running pods are terminated.\n- FailIndex: indicates that the pod's index is marked as Failed and will\n not be restarted.\n This value is alpha-level. It can be used when the\n `JobBackoffLimitPerIndex` feature gate is enabled (disabled by default).\n- Ignore: indicates that the counter towards the .backoffLimit is not\n incremented and a replacement pod is created.\n- Count: indicates that the pod is handled in the default way - the\n counter towards the .backoffLimit is incremented.\nAdditional values are considered to be added in the future. Clients should react to an unknown action by skipping the rule.", + "type": "string" + }, + "onExitCodes": { + "$ref": "#/definitions/io.k8s.api.batch.v1.PodFailurePolicyOnExitCodesRequirement", + "description": "Represents the requirement on the container exit codes." + }, + "onPodConditions": { + "description": "Represents the requirement on the pod conditions. The requirement is represented as a list of pod condition patterns. The requirement is satisfied if at least one pattern matches an actual pod condition. At most 20 elements are allowed.", + "items": { + "$ref": "#/definitions/io.k8s.api.batch.v1.PodFailurePolicyOnPodConditionsPattern" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "action" + ], + "type": "object" + }, + "io.k8s.api.batch.v1.UncountedTerminatedPods": { + "description": "UncountedTerminatedPods holds UIDs of Pods that have terminated but haven't been accounted in Job status counters.", + "properties": { + "failed": { + "description": "failed holds UIDs of failed Pods.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "succeeded": { + "description": "succeeded holds UIDs of succeeded Pods.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "type": "object" + }, + "io.k8s.api.certificates.v1.CertificateSigningRequest": { + "description": "CertificateSigningRequest objects provide a mechanism to obtain x509 certificates by submitting a certificate signing request, and having it asynchronously approved and issued.\n\nKubelets use this API to obtain:\n 1. client certificates to authenticate to kube-apiserver (with the \"kubernetes.io/kube-apiserver-client-kubelet\" signerName).\n 2. serving certificates for TLS endpoints kube-apiserver can connect to securely (with the \"kubernetes.io/kubelet-serving\" signerName).\n\nThis API can be used to request client certificates to authenticate to kube-apiserver (with the \"kubernetes.io/kube-apiserver-client\" signerName), or to obtain certificates from custom non-Kubernetes signers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CertificateSigningRequest" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.certificates.v1.CertificateSigningRequestSpec", + "description": "spec contains the certificate request, and is immutable after creation. Only the request, signerName, expirationSeconds, and usages fields can be set on creation. Other fields are derived by Kubernetes and cannot be modified by users." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.certificates.v1.CertificateSigningRequestStatus", + "description": "status contains information about whether the request is approved or denied, and the certificate issued by the signer, or the failure condition indicating signer failure." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "CertificateSigningRequest", + "version": "v1" + } + ] + }, + "io.k8s.api.certificates.v1.CertificateSigningRequestCondition": { + "description": "CertificateSigningRequestCondition describes a condition of a CertificateSigningRequest object", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime is the time the condition last transitioned from one status to another. If unset, when a new condition type is added or an existing condition's status is changed, the server defaults this to the current time." + }, + "lastUpdateTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastUpdateTime is the time of the last update to this condition" + }, + "message": { + "description": "message contains a human readable message with details about the request state", + "type": "string" + }, + "reason": { + "description": "reason indicates a brief reason for the request state", + "type": "string" + }, + "status": { + "description": "status of the condition, one of True, False, Unknown. Approved, Denied, and Failed conditions may not be \"False\" or \"Unknown\".", + "type": "string" + }, + "type": { + "description": "type of the condition. Known conditions are \"Approved\", \"Denied\", and \"Failed\".\n\nAn \"Approved\" condition is added via the /approval subresource, indicating the request was approved and should be issued by the signer.\n\nA \"Denied\" condition is added via the /approval subresource, indicating the request was denied and should not be issued by the signer.\n\nA \"Failed\" condition is added via the /status subresource, indicating the signer failed to issue the certificate.\n\nApproved and Denied conditions are mutually exclusive. Approved, Denied, and Failed conditions cannot be removed once added.\n\nOnly one condition of a given type is allowed.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.certificates.v1.CertificateSigningRequestList": { + "description": "CertificateSigningRequestList is a collection of CertificateSigningRequest objects", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a collection of CertificateSigningRequest objects", + "items": { + "$ref": "#/definitions/io.k8s.api.certificates.v1.CertificateSigningRequest" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CertificateSigningRequestList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "CertificateSigningRequestList", + "version": "v1" + } + ] + }, + "io.k8s.api.certificates.v1.CertificateSigningRequestSpec": { + "description": "CertificateSigningRequestSpec contains the certificate request.", + "properties": { + "expirationSeconds": { + "description": "expirationSeconds is the requested duration of validity of the issued certificate. The certificate signer may issue a certificate with a different validity duration so a client must check the delta between the notBefore and and notAfter fields in the issued certificate to determine the actual duration.\n\nThe v1.22+ in-tree implementations of the well-known Kubernetes signers will honor this field as long as the requested duration is not greater than the maximum duration they will honor per the --cluster-signing-duration CLI flag to the Kubernetes controller manager.\n\nCertificate signers may not honor this field for various reasons:\n\n 1. Old signer that is unaware of the field (such as the in-tree\n implementations prior to v1.22)\n 2. Signer whose configured maximum is shorter than the requested duration\n 3. Signer whose configured minimum is longer than the requested duration\n\nThe minimum valid value for expirationSeconds is 600, i.e. 10 minutes.", + "format": "int32", + "type": "integer" + }, + "extra": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": "extra contains extra attributes of the user that created the CertificateSigningRequest. Populated by the API server on creation and immutable.", + "type": "object" + }, + "groups": { + "description": "groups contains group membership of the user that created the CertificateSigningRequest. Populated by the API server on creation and immutable.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "request": { + "description": "request contains an x509 certificate signing request encoded in a \"CERTIFICATE REQUEST\" PEM block. When serialized as JSON or YAML, the data is additionally base64-encoded.", + "format": "byte", + "type": "string", + "x-kubernetes-list-type": "atomic" + }, + "signerName": { + "description": "signerName indicates the requested signer, and is a qualified name.\n\nList/watch requests for CertificateSigningRequests can filter on this field using a \"spec.signerName=NAME\" fieldSelector.\n\nWell-known Kubernetes signers are:\n 1. \"kubernetes.io/kube-apiserver-client\": issues client certificates that can be used to authenticate to kube-apiserver.\n Requests for this signer are never auto-approved by kube-controller-manager, can be issued by the \"csrsigning\" controller in kube-controller-manager.\n 2. \"kubernetes.io/kube-apiserver-client-kubelet\": issues client certificates that kubelets use to authenticate to kube-apiserver.\n Requests for this signer can be auto-approved by the \"csrapproving\" controller in kube-controller-manager, and can be issued by the \"csrsigning\" controller in kube-controller-manager.\n 3. \"kubernetes.io/kubelet-serving\" issues serving certificates that kubelets use to serve TLS endpoints, which kube-apiserver can connect to securely.\n Requests for this signer are never auto-approved by kube-controller-manager, and can be issued by the \"csrsigning\" controller in kube-controller-manager.\n\nMore details are available at https://k8s.io/docs/reference/access-authn-authz/certificate-signing-requests/#kubernetes-signers\n\nCustom signerNames can also be specified. The signer defines:\n 1. Trust distribution: how trust (CA bundles) are distributed.\n 2. Permitted subjects: and behavior when a disallowed subject is requested.\n 3. Required, permitted, or forbidden x509 extensions in the request (including whether subjectAltNames are allowed, which types, restrictions on allowed values) and behavior when a disallowed extension is requested.\n 4. Required, permitted, or forbidden key usages / extended key usages.\n 5. Expiration/certificate lifetime: whether it is fixed by the signer, configurable by the admin.\n 6. Whether or not requests for CA certificates are allowed.", + "type": "string" + }, + "uid": { + "description": "uid contains the uid of the user that created the CertificateSigningRequest. Populated by the API server on creation and immutable.", + "type": "string" + }, + "usages": { + "description": "usages specifies a set of key usages requested in the issued certificate.\n\nRequests for TLS client certificates typically request: \"digital signature\", \"key encipherment\", \"client auth\".\n\nRequests for TLS serving certificates typically request: \"key encipherment\", \"digital signature\", \"server auth\".\n\nValid values are:\n \"signing\", \"digital signature\", \"content commitment\",\n \"key encipherment\", \"key agreement\", \"data encipherment\",\n \"cert sign\", \"crl sign\", \"encipher only\", \"decipher only\", \"any\",\n \"server auth\", \"client auth\",\n \"code signing\", \"email protection\", \"s/mime\",\n \"ipsec end system\", \"ipsec tunnel\", \"ipsec user\",\n \"timestamping\", \"ocsp signing\", \"microsoft sgc\", \"netscape sgc\"", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "username": { + "description": "username contains the name of the user that created the CertificateSigningRequest. Populated by the API server on creation and immutable.", + "type": "string" + } + }, + "required": [ + "request", + "signerName" + ], + "type": "object" + }, + "io.k8s.api.certificates.v1.CertificateSigningRequestStatus": { + "description": "CertificateSigningRequestStatus contains conditions used to indicate approved/denied/failed status of the request, and the issued certificate.", + "properties": { + "certificate": { + "description": "certificate is populated with an issued certificate by the signer after an Approved condition is present. This field is set via the /status subresource. Once populated, this field is immutable.\n\nIf the certificate signing request is denied, a condition of type \"Denied\" is added and this field remains empty. If the signer cannot issue the certificate, a condition of type \"Failed\" is added and this field remains empty.\n\nValidation requirements:\n 1. certificate must contain one or more PEM blocks.\n 2. All PEM blocks must have the \"CERTIFICATE\" label, contain no headers, and the encoded data\n must be a BER-encoded ASN.1 Certificate structure as described in section 4 of RFC5280.\n 3. Non-PEM content may appear before or after the \"CERTIFICATE\" PEM blocks and is unvalidated,\n to allow for explanatory text as described in section 5.2 of RFC7468.\n\nIf more than one PEM block is present, and the definition of the requested spec.signerName does not indicate otherwise, the first block is the issued certificate, and subsequent blocks should be treated as intermediate certificates and presented in TLS handshakes.\n\nThe certificate is encoded in PEM format.\n\nWhen serialized as JSON or YAML, the data is additionally base64-encoded, so it consists of:\n\n base64(\n -----BEGIN CERTIFICATE-----\n ...\n -----END CERTIFICATE-----\n )", + "format": "byte", + "type": "string", + "x-kubernetes-list-type": "atomic" + }, + "conditions": { + "description": "conditions applied to the request. Known conditions are \"Approved\", \"Denied\", and \"Failed\".", + "items": { + "$ref": "#/definitions/io.k8s.api.certificates.v1.CertificateSigningRequestCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + } + }, + "type": "object" + }, + "io.k8s.api.certificates.v1alpha1.ClusterTrustBundle": { + "description": "ClusterTrustBundle is a cluster-scoped container for X.509 trust anchors (root certificates).\n\nClusterTrustBundle objects are considered to be readable by any authenticated user in the cluster, because they can be mounted by pods using the `clusterTrustBundle` projection. All service accounts have read access to ClusterTrustBundles by default. Users who only have namespace-level access to a cluster can read ClusterTrustBundles by impersonating a serviceaccount that they have access to.\n\nIt can be optionally associated with a particular assigner, in which case it contains one valid set of trust anchors for that signer. Signers may have multiple associated ClusterTrustBundles; each is an independent set of trust anchors for that signer. Admission control is used to enforce that only users with permissions on the signer can create or modify the corresponding bundle.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterTrustBundle" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "metadata contains the object metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.ClusterTrustBundleSpec", + "description": "spec contains the signer (if any) and trust anchors." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundle", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.certificates.v1alpha1.ClusterTrustBundleList": { + "description": "ClusterTrustBundleList is a collection of ClusterTrustBundle objects", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a collection of ClusterTrustBundle objects", + "items": { + "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.ClusterTrustBundle" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterTrustBundleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "metadata contains the list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundleList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.certificates.v1alpha1.ClusterTrustBundleSpec": { + "description": "ClusterTrustBundleSpec contains the signer and trust anchors.", + "properties": { + "signerName": { + "description": "signerName indicates the associated signer, if any.\n\nIn order to create or update a ClusterTrustBundle that sets signerName, you must have the following cluster-scoped permission: group=certificates.k8s.io resource=signers resourceName= verb=attest.\n\nIf signerName is not empty, then the ClusterTrustBundle object must be named with the signer name as a prefix (translating slashes to colons). For example, for the signer name `example.com/foo`, valid ClusterTrustBundle object names include `example.com:foo:abc` and `example.com:foo:v1`.\n\nIf signerName is empty, then the ClusterTrustBundle object's name must not have such a prefix.\n\nList/watch requests for ClusterTrustBundles can filter on this field using a `spec.signerName=NAME` field selector.", + "type": "string" + }, + "trustBundle": { + "description": "trustBundle contains the individual X.509 trust anchors for this bundle, as PEM bundle of PEM-wrapped, DER-formatted X.509 certificates.\n\nThe data must consist only of PEM certificate blocks that parse as valid X.509 certificates. Each certificate must include a basic constraints extension with the CA bit set. The API server will reject objects that contain duplicate certificates, or that use PEM block headers.\n\nUsers of ClusterTrustBundles, including Kubelet, are free to reorder and deduplicate certificate blocks in this file according to their own logic, as well as to drop PEM block headers and inter-block data.", + "type": "string" + } + }, + "required": [ + "trustBundle" + ], + "type": "object" + }, + "io.k8s.api.coordination.v1.Lease": { + "description": "Lease defines a lease concept.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Lease" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.coordination.v1.LeaseSpec", + "description": "spec contains the specification of the Lease. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "coordination.k8s.io", + "kind": "Lease", + "version": "v1" + } + ] + }, + "io.k8s.api.coordination.v1.LeaseList": { + "description": "LeaseList is a list of Lease objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.coordination.v1.Lease" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LeaseList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "coordination.k8s.io", + "kind": "LeaseList", + "version": "v1" + } + ] + }, + "io.k8s.api.coordination.v1.LeaseSpec": { + "description": "LeaseSpec is a specification of a Lease.", + "properties": { + "acquireTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "acquireTime is a time when the current lease was acquired." + }, + "holderIdentity": { + "description": "holderIdentity contains the identity of the holder of a current lease.", + "type": "string" + }, + "leaseDurationSeconds": { + "description": "leaseDurationSeconds is a duration that candidates for a lease need to wait to force acquire it. This is measure against time of last observed renewTime.", + "format": "int32", + "type": "integer" + }, + "leaseTransitions": { + "description": "leaseTransitions is the number of transitions of a lease between holders.", + "format": "int32", + "type": "integer" + }, + "renewTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "renewTime is a time when the current holder of a lease has last updated the lease." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource": { + "description": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + "type": "string" + }, + "partition": { + "description": "partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).", + "format": "int32", + "type": "integer" + }, + "readOnly": { + "description": "readOnly value true will force the readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + "type": "boolean" + }, + "volumeID": { + "description": "volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Affinity": { + "description": "Affinity is a group of affinity scheduling rules.", + "properties": { + "nodeAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeAffinity", + "description": "Describes node affinity scheduling rules for the pod." + }, + "podAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinity", + "description": "Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s))." + }, + "podAntiAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAntiAffinity", + "description": "Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s))." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.AttachedVolume": { + "description": "AttachedVolume describes a volume attached to a node", + "properties": { + "devicePath": { + "description": "DevicePath represents the device path where the volume should be available", + "type": "string" + }, + "name": { + "description": "Name of the attached volume", + "type": "string" + } + }, + "required": [ + "name", + "devicePath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.AzureDiskVolumeSource": { + "description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + "properties": { + "cachingMode": { + "description": "cachingMode is the Host Caching mode: None, Read Only, Read Write.", + "type": "string" + }, + "diskName": { + "description": "diskName is the Name of the data disk in the blob storage", + "type": "string" + }, + "diskURI": { + "description": "diskURI is the URI of data disk in the blob storage", + "type": "string" + }, + "fsType": { + "description": "fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "kind": { + "description": "kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared", + "type": "string" + }, + "readOnly": { + "description": "readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + } + }, + "required": [ + "diskName", + "diskURI" + ], + "type": "object" + }, + "io.k8s.api.core.v1.AzureFilePersistentVolumeSource": { + "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + "properties": { + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretName": { + "description": "secretName is the name of secret that contains Azure Storage Account Name and Key", + "type": "string" + }, + "secretNamespace": { + "description": "secretNamespace is the namespace of the secret that contains Azure Storage Account Name and Key default is the same as the Pod", + "type": "string" + }, + "shareName": { + "description": "shareName is the azure Share Name", + "type": "string" + } + }, + "required": [ + "secretName", + "shareName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.AzureFileVolumeSource": { + "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + "properties": { + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretName": { + "description": "secretName is the name of secret that contains Azure Storage Account Name and Key", + "type": "string" + }, + "shareName": { + "description": "shareName is the azure share Name", + "type": "string" + } + }, + "required": [ + "secretName", + "shareName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Binding": { + "description": "Binding ties one object to another; for example, a pod is bound to a node by a scheduler. Deprecated in 1.7, please use the bindings subresource of pods instead.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Binding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "target": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "The target object that you want to bind to the standard object." + } + }, + "required": [ + "target" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Binding", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.CSIPersistentVolumeSource": { + "description": "Represents storage that is managed by an external CSI volume driver (Beta feature)", + "properties": { + "controllerExpandSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "controllerExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerExpandVolume call. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "controllerPublishSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "controllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "driver": { + "description": "driver is the name of the driver to use for this volume. Required.", + "type": "string" + }, + "fsType": { + "description": "fsType to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".", + "type": "string" + }, + "nodeExpandSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "nodeExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeExpandVolume call. This is a beta field which is enabled default by CSINodeExpandSecret feature gate. This field is optional, may be omitted if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "nodePublishSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "nodeStageSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "nodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed." + }, + "readOnly": { + "description": "readOnly value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", + "type": "boolean" + }, + "volumeAttributes": { + "additionalProperties": { + "type": "string" + }, + "description": "volumeAttributes of the volume to publish.", + "type": "object" + }, + "volumeHandle": { + "description": "volumeHandle is the unique volume name returned by the CSI volume plugin’s CreateVolume to refer to the volume on all subsequent calls. Required.", + "type": "string" + } + }, + "required": [ + "driver", + "volumeHandle" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CSIVolumeSource": { + "description": "Represents a source location of a volume to mount, managed by an external CSI driver", + "properties": { + "driver": { + "description": "driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.", + "type": "string" + }, + "fsType": { + "description": "fsType to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.", + "type": "string" + }, + "nodePublishSecretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed." + }, + "readOnly": { + "description": "readOnly specifies a read-only configuration for the volume. Defaults to false (read/write).", + "type": "boolean" + }, + "volumeAttributes": { + "additionalProperties": { + "type": "string" + }, + "description": "volumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.", + "type": "object" + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Capabilities": { + "description": "Adds and removes POSIX capabilities from running containers.", + "properties": { + "add": { + "description": "Added capabilities", + "items": { + "type": "string" + }, + "type": "array" + }, + "drop": { + "description": "Removed capabilities", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.CephFSPersistentVolumeSource": { + "description": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "monitors": { + "description": "monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "path": { + "description": "path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + "type": "string" + }, + "readOnly": { + "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "type": "boolean" + }, + "secretFile": { + "description": "secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it" + }, + "user": { + "description": "user is Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CephFSVolumeSource": { + "description": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "monitors": { + "description": "monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "path": { + "description": "path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + "type": "string" + }, + "readOnly": { + "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "type": "boolean" + }, + "secretFile": { + "description": "secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it" + }, + "user": { + "description": "user is optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CinderPersistentVolumeSource": { + "description": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + "type": "string" + }, + "readOnly": { + "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "secretRef is Optional: points to a secret object containing parameters used to connect to OpenStack." + }, + "volumeID": { + "description": "volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.CinderVolumeSource": { + "description": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + "type": "string" + }, + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef is optional: points to a secret object containing parameters used to connect to OpenStack." + }, + "volumeID": { + "description": "volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ClaimSource": { + "description": "ClaimSource describes a reference to a ResourceClaim.\n\nExactly one of these fields should be set. Consumers of this type must treat an empty object as if it has an unknown value.", + "properties": { + "resourceClaimName": { + "description": "ResourceClaimName is the name of a ResourceClaim object in the same namespace as this pod.", + "type": "string" + }, + "resourceClaimTemplateName": { + "description": "ResourceClaimTemplateName is the name of a ResourceClaimTemplate object in the same namespace as this pod.\n\nThe template will be used to create a new ResourceClaim, which will be bound to this pod. When this pod is deleted, the ResourceClaim will also be deleted. The pod name and resource name, along with a generated component, will be used to form a unique name for the ResourceClaim, which will be recorded in pod.status.resourceClaimStatuses.\n\nThis field is immutable and no changes will be made to the corresponding ResourceClaim by the control plane after creating the ResourceClaim.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ClientIPConfig": { + "description": "ClientIPConfig represents the configurations of Client IP based session affinity.", + "properties": { + "timeoutSeconds": { + "description": "timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\". Default value is 10800(for 3 hours).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ComponentCondition": { + "description": "Information about the condition of a component.", + "properties": { + "error": { + "description": "Condition error code for a component. For example, a health check error code.", + "type": "string" + }, + "message": { + "description": "Message about the condition for a component. For example, information about a health check.", + "type": "string" + }, + "status": { + "description": "Status of the condition for a component. Valid values for \"Healthy\": \"True\", \"False\", or \"Unknown\".", + "type": "string" + }, + "type": { + "description": "Type of condition for a component. Valid value: \"Healthy\"", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ComponentStatus": { + "description": "ComponentStatus (and ComponentStatusList) holds the cluster validation info. Deprecated: This API is deprecated in v1.19+", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "conditions": { + "description": "List of component conditions observed", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ComponentCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ComponentStatus" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ComponentStatus", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ComponentStatusList": { + "description": "Status of all the conditions for the component as a list of ComponentStatus objects. Deprecated: This API is deprecated in v1.19+", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ComponentStatus objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ComponentStatus" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ComponentStatusList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ComponentStatusList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ConfigMap": { + "description": "ConfigMap holds configuration data for pods to consume.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "binaryData": { + "additionalProperties": { + "format": "byte", + "type": "string" + }, + "description": "BinaryData contains the binary data. Each key must consist of alphanumeric characters, '-', '_' or '.'. BinaryData can contain byte sequences that are not in the UTF-8 range. The keys stored in BinaryData must not overlap with the ones in the Data field, this is enforced during validation process. Using this field will require 1.10+ apiserver and kubelet.", + "type": "object" + }, + "data": { + "additionalProperties": { + "type": "string" + }, + "description": "Data contains the configuration data. Each key must consist of alphanumeric characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use the BinaryData field. The keys stored in Data must not overlap with the keys in the BinaryData field, this is enforced during validation process.", + "type": "object" + }, + "immutable": { + "description": "Immutable, if set to true, ensures that data stored in the ConfigMap cannot be updated (only object metadata can be modified). If not set to true, the field can be modified at any time. Defaulted to nil.", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ConfigMap" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ConfigMap", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ConfigMapEnvSource": { + "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the ConfigMap must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapKeySelector": { + "description": "Selects a key from a ConfigMap.", + "properties": { + "key": { + "description": "The key to select.", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the ConfigMap or its key must be defined", + "type": "boolean" + } + }, + "required": [ + "key" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.ConfigMapList": { + "description": "ConfigMapList is a resource containing a list of ConfigMap objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of ConfigMaps.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMap" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ConfigMapList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ConfigMapList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ConfigMapNodeConfigSource": { + "description": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node. This API is deprecated since 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration", + "properties": { + "kubeletConfigKey": { + "description": "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.", + "type": "string" + }, + "name": { + "description": "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.", + "type": "string" + }, + "namespace": { + "description": "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.", + "type": "string" + }, + "resourceVersion": { + "description": "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + "type": "string" + }, + "uid": { + "description": "UID is the metadata.UID of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + "type": "string" + } + }, + "required": [ + "namespace", + "name", + "kubeletConfigKey" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapProjection": { + "description": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.", + "properties": { + "items": { + "description": "items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "optional specify whether the ConfigMap or its keys must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ConfigMapVolumeSource": { + "description": "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.", + "properties": { + "defaultMode": { + "description": "defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "items": { + "description": "items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "optional specify whether the ConfigMap or its keys must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Container": { + "description": "A single application container that you want to run within a pod.", + "properties": { + "args": { + "description": "Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "items": { + "type": "string" + }, + "type": "array" + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "items": { + "type": "string" + }, + "type": "array" + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvVar" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvFromSource" + }, + "type": "array" + }, + "image": { + "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "$ref": "#/definitions/io.k8s.api.core.v1.Lifecycle", + "description": "Actions that the management system should take in response to container lifecycle events. Cannot be updated." + }, + "livenessProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" + }, + "name": { + "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + "type": "string" + }, + "ports": { + "description": "List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" + }, + "resizePolicy": { + "description": "Resources resize policy for the container.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerResizePolicy" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/" + }, + "restartPolicy": { + "description": "RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is \"Always\". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.", + "type": "string" + }, + "securityContext": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecurityContext", + "description": "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" + }, + "startupProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeDevice" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerImage": { + "description": "Describe a container image", + "properties": { + "names": { + "description": "Names by which this image is known. e.g. [\"kubernetes.example/hyperkube:v1.0.7\", \"cloud-vendor.registry.example/cloud-vendor/hyperkube:v1.0.7\"]", + "items": { + "type": "string" + }, + "type": "array" + }, + "sizeBytes": { + "description": "The size of the image in bytes.", + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerPort": { + "description": "ContainerPort represents a network port in a single container.", + "properties": { + "containerPort": { + "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.", + "format": "int32", + "type": "integer" + }, + "hostIP": { + "description": "What host IP to bind the external port to.", + "type": "string" + }, + "hostPort": { + "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.", + "format": "int32", + "type": "integer" + }, + "name": { + "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.", + "type": "string" + }, + "protocol": { + "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".", + "type": "string" + } + }, + "required": [ + "containerPort" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerResizePolicy": { + "description": "ContainerResizePolicy represents resource resize policy for the container.", + "properties": { + "resourceName": { + "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.", + "type": "string" + }, + "restartPolicy": { + "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.", + "type": "string" + } + }, + "required": [ + "resourceName", + "restartPolicy" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerState": { + "description": "ContainerState holds a possible state of container. Only one of its members may be specified. If none of them is specified, the default one is ContainerStateWaiting.", + "properties": { + "running": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStateRunning", + "description": "Details about a running container" + }, + "terminated": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStateTerminated", + "description": "Details about a terminated container" + }, + "waiting": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStateWaiting", + "description": "Details about a waiting container" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStateRunning": { + "description": "ContainerStateRunning is a running state of a container.", + "properties": { + "startedAt": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time at which the container was last (re-)started" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStateTerminated": { + "description": "ContainerStateTerminated is a terminated state of a container.", + "properties": { + "containerID": { + "description": "Container's ID in the format '://'", + "type": "string" + }, + "exitCode": { + "description": "Exit status from the last termination of the container", + "format": "int32", + "type": "integer" + }, + "finishedAt": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time at which the container last terminated" + }, + "message": { + "description": "Message regarding the last termination of the container", + "type": "string" + }, + "reason": { + "description": "(brief) reason from the last termination of the container", + "type": "string" + }, + "signal": { + "description": "Signal from the last termination of the container", + "format": "int32", + "type": "integer" + }, + "startedAt": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time at which previous execution of the container started" + } + }, + "required": [ + "exitCode" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStateWaiting": { + "description": "ContainerStateWaiting is a waiting state of a container.", + "properties": { + "message": { + "description": "Message regarding why the container is not yet running.", + "type": "string" + }, + "reason": { + "description": "(brief) reason the container is not yet running.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerStatus": { + "description": "ContainerStatus contains details for the current status of this container.", + "properties": { + "allocatedResources": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "AllocatedResources represents the compute resources allocated for this container by the node. Kubelet sets this value to Container.Resources.Requests upon successful pod admission and after successfully admitting desired pod resize.", + "type": "object" + }, + "containerID": { + "description": "ContainerID is the ID of the container in the format '://'. Where type is a container runtime identifier, returned from Version call of CRI API (for example \"containerd\").", + "type": "string" + }, + "image": { + "description": "Image is the name of container image that the container is running. The container image may not match the image used in the PodSpec, as it may have been resolved by the runtime. More info: https://kubernetes.io/docs/concepts/containers/images.", + "type": "string" + }, + "imageID": { + "description": "ImageID is the image ID of the container's image. The image ID may not match the image ID of the image used in the PodSpec, as it may have been resolved by the runtime.", + "type": "string" + }, + "lastState": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerState", + "description": "LastTerminationState holds the last termination state of the container to help debug container crashes and restarts. This field is not populated if the container is still running and RestartCount is 0." + }, + "name": { + "description": "Name is a DNS_LABEL representing the unique name of the container. Each container in a pod must have a unique name across all container types. Cannot be updated.", + "type": "string" + }, + "ready": { + "description": "Ready specifies whether the container is currently passing its readiness check. The value will change as readiness probes keep executing. If no readiness probes are specified, this field defaults to true once the container is fully started (see Started field).\n\nThe value is typically used to determine whether a container is ready to accept traffic.", + "type": "boolean" + }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Resources represents the compute resource requests and limits that have been successfully enacted on the running container after it has been started or has been successfully resized." + }, + "restartCount": { + "description": "RestartCount holds the number of times the container has been restarted. Kubelet makes an effort to always increment the value, but there are cases when the state may be lost due to node restarts and then the value may be reset to 0. The value is never negative.", + "format": "int32", + "type": "integer" + }, + "started": { + "description": "Started indicates whether the container has finished its postStart lifecycle hook and passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. In both cases, startup probes will run again. Is always true when no startupProbe is defined and container is running and has passed the postStart lifecycle hook. The null value must be treated the same as false.", + "type": "boolean" + }, + "state": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerState", + "description": "State holds details about the container's current condition." + } + }, + "required": [ + "name", + "ready", + "restartCount", + "image", + "imageID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.DaemonEndpoint": { + "description": "DaemonEndpoint contains information about a single Daemon endpoint.", + "properties": { + "Port": { + "description": "Port number of the given endpoint.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "Port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.DownwardAPIProjection": { + "description": "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.", + "properties": { + "items": { + "description": "Items is a list of DownwardAPIVolume file", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeFile" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.DownwardAPIVolumeFile": { + "description": "DownwardAPIVolumeFile represents information to create the file containing the pod field", + "properties": { + "fieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectFieldSelector", + "description": "Required: Selects a field of the pod: only annotations, labels, name and namespace are supported." + }, + "mode": { + "description": "Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "path": { + "description": "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'", + "type": "string" + }, + "resourceFieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceFieldSelector", + "description": "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported." + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.DownwardAPIVolumeSource": { + "description": "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.", + "properties": { + "defaultMode": { + "description": "Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "items": { + "description": "Items is a list of downward API volume file", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeFile" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EmptyDirVolumeSource": { + "description": "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.", + "properties": { + "medium": { + "description": "medium represents what type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + "type": "string" + }, + "sizeLimit": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EndpointAddress": { + "description": "EndpointAddress is a tuple that describes single IP address.", + "properties": { + "hostname": { + "description": "The Hostname of this endpoint", + "type": "string" + }, + "ip": { + "description": "The IP of this endpoint. May not be loopback (127.0.0.0/8 or ::1), link-local (169.254.0.0/16 or fe80::/10), or link-local multicast (224.0.0.0/24 or ff02::/16).", + "type": "string" + }, + "nodeName": { + "description": "Optional: Node hosting this endpoint. This can be used to determine endpoints local to a node.", + "type": "string" + }, + "targetRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "Reference to object providing the endpoint." + } + }, + "required": [ + "ip" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.EndpointPort": { + "description": "EndpointPort is a tuple that describes a single port.", + "properties": { + "appProtocol": { + "description": "The application protocol for this port. This is used as a hint for implementations to offer richer behavior for protocols that they understand. This field follows standard Kubernetes label syntax. Valid values are either:\n\n* Un-prefixed protocol names - reserved for IANA standard service names (as per RFC-6335 and https://www.iana.org/assignments/service-names).\n\n* Kubernetes-defined prefixed names:\n * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455\n * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455\n\n* Other protocols should use implementation-defined prefixed names such as mycompany.com/my-custom-protocol.", + "type": "string" + }, + "name": { + "description": "The name of this port. This must match the 'name' field in the corresponding ServicePort. Must be a DNS_LABEL. Optional only if one port is defined.", + "type": "string" + }, + "port": { + "description": "The port number of the endpoint.", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", + "type": "string" + } + }, + "required": [ + "port" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.EndpointSubset": { + "description": "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n\n\t{\n\t Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n\t Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n\t}\n\nThe resulting set of endpoints can be viewed as:\n\n\ta: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n\tb: [ 10.10.1.1:309, 10.10.2.2:309 ]", + "properties": { + "addresses": { + "description": "IP addresses which offer the related ports that are marked as ready. These endpoints should be considered safe for load balancers and clients to utilize.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointAddress" + }, + "type": "array" + }, + "notReadyAddresses": { + "description": "IP addresses which offer the related ports but are not currently marked as ready because they have not yet finished starting, have recently failed a readiness check, or have recently failed a liveness check.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointAddress" + }, + "type": "array" + }, + "ports": { + "description": "Port numbers available on the related IP addresses.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointPort" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Endpoints": { + "description": "Endpoints is a collection of endpoints that implement the actual service. Example:\n\n\t Name: \"mysvc\",\n\t Subsets: [\n\t {\n\t Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n\t Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n\t },\n\t {\n\t Addresses: [{\"ip\": \"10.10.3.3\"}],\n\t Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n\t },\n\t]", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Endpoints" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "subsets": { + "description": "The set of all endpoints is the union of all subsets. Addresses are placed into subsets according to the IPs they share. A single address with multiple ports, some of which are ready and some of which are not (because they come from different containers) will result in the address being displayed in different subsets for the different ports. No address will appear in both Addresses and NotReadyAddresses in the same subset. Sets of addresses and ports that comprise a service.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EndpointSubset" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Endpoints", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EndpointsList": { + "description": "EndpointsList is a list of endpoints.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of endpoints.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Endpoints" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EndpointsList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "EndpointsList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EnvFromSource": { + "description": "EnvFromSource represents the source of a set of ConfigMaps", + "properties": { + "configMapRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapEnvSource", + "description": "The ConfigMap to select from" + }, + "prefix": { + "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.", + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretEnvSource", + "description": "The Secret to select from" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EnvVar": { + "description": "EnvVar represents an environment variable present in a Container.", + "properties": { + "name": { + "description": "Name of the environment variable. Must be a C_IDENTIFIER.", + "type": "string" + }, + "value": { + "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", + "type": "string" + }, + "valueFrom": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvVarSource", + "description": "Source for the environment variable's value. Cannot be used if value is not empty." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.EnvVarSource": { + "description": "EnvVarSource represents a source for the value of an EnvVar.", + "properties": { + "configMapKeyRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapKeySelector", + "description": "Selects a key of a ConfigMap." + }, + "fieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectFieldSelector", + "description": "Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs." + }, + "resourceFieldRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceFieldSelector", + "description": "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported." + }, + "secretKeyRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretKeySelector", + "description": "Selects a key of a secret in the pod's namespace" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EphemeralContainer": { + "description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.", + "properties": { + "args": { + "description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "items": { + "type": "string" + }, + "type": "array" + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "items": { + "type": "string" + }, + "type": "array" + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvVar" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EnvFromSource" + }, + "type": "array" + }, + "image": { + "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "$ref": "#/definitions/io.k8s.api.core.v1.Lifecycle", + "description": "Lifecycle is not allowed for ephemeral containers." + }, + "livenessProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Probes are not allowed for ephemeral containers." + }, + "name": { + "description": "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", + "type": "string" + }, + "ports": { + "description": "Ports are not allowed for ephemeral containers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + }, + "readinessProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Probes are not allowed for ephemeral containers." + }, + "resizePolicy": { + "description": "Resources resize policy for the container.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerResizePolicy" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod." + }, + "restartPolicy": { + "description": "Restart policy for the container to manage the restart behavior of each container within a pod. This may only be set for init containers. You cannot set this field on ephemeral containers.", + "type": "string" + }, + "securityContext": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecurityContext", + "description": "Optional: SecurityContext defines the security options the ephemeral container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext." + }, + "startupProbe": { + "$ref": "#/definitions/io.k8s.api.core.v1.Probe", + "description": "Probes are not allowed for ephemeral containers." + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "targetContainerName": { + "description": "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container uses the namespaces configured in the Pod spec.\n\nThe container runtime must implement support for this feature. If the runtime does not support namespace targeting then the result of setting this field is undefined.", + "type": "string" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeDevice" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge" + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Subpath mounts are not allowed for ephemeral containers. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge" + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.EphemeralVolumeSource": { + "description": "Represents an ephemeral volume that is handled by a normal storage driver.", + "properties": { + "volumeClaimTemplate": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimTemplate", + "description": "Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long).\n\nAn existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster.\n\nThis field is read-only and no changes will be made by Kubernetes to the PVC after it has been created.\n\nRequired, must not be nil." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Event": { + "description": "Event is a report of an event somewhere in the cluster. Events have a limited retention time and triggers and messages may evolve with time. Event consumers should not rely on the timing of an event with a given Reason reflecting a consistent underlying trigger, or the continued existence of events with that Reason. Events should be treated as informative, best-effort, supplemental data.", + "properties": { + "action": { + "description": "What action was taken/failed regarding to the Regarding object.", + "type": "string" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "count": { + "description": "The number of times this event has occurred.", + "format": "int32", + "type": "integer" + }, + "eventTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "Time when this Event was first observed." + }, + "firstTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The time at which the event was first recorded. (Time of server receipt is in TypeMeta.)" + }, + "involvedObject": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "The object that this event is about." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Event" + ] + }, + "lastTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The time at which the most recent occurrence of this event was recorded." + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "reason": { + "description": "This should be a short, machine understandable string that gives the reason for the transition into the object's current status.", + "type": "string" + }, + "related": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "Optional secondary object for more complex actions." + }, + "reportingComponent": { + "description": "Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`.", + "type": "string" + }, + "reportingInstance": { + "description": "ID of the controller instance, e.g. `kubelet-xyzf`.", + "type": "string" + }, + "series": { + "$ref": "#/definitions/io.k8s.api.core.v1.EventSeries", + "description": "Data about the Event series this event represents or nil if it's a singleton Event." + }, + "source": { + "$ref": "#/definitions/io.k8s.api.core.v1.EventSource", + "description": "The component reporting this event. Should be a short machine understandable string." + }, + "type": { + "description": "Type of this event (Normal, Warning), new types could be added in the future", + "type": "string" + } + }, + "required": [ + "metadata", + "involvedObject" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Event", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EventList": { + "description": "EventList is a list of events.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of events", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Event" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EventList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "EventList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.EventSeries": { + "description": "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time.", + "properties": { + "count": { + "description": "Number of occurrences in this series up to the last heartbeat time", + "format": "int32", + "type": "integer" + }, + "lastObservedTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "Time of the last occurrence observed" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.EventSource": { + "description": "EventSource contains information for an event.", + "properties": { + "component": { + "description": "Component from which the event is generated.", + "type": "string" + }, + "host": { + "description": "Node name on which the event is generated.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ExecAction": { + "description": "ExecAction describes a \"run in container\" action.", + "properties": { + "command": { + "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.FCVolumeSource": { + "description": "Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "lun": { + "description": "lun is Optional: FC target lun number", + "format": "int32", + "type": "integer" + }, + "readOnly": { + "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "targetWWNs": { + "description": "targetWWNs is Optional: FC target worldwide names (WWNs)", + "items": { + "type": "string" + }, + "type": "array" + }, + "wwids": { + "description": "wwids Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.FlexPersistentVolumeSource": { + "description": "FlexPersistentVolumeSource represents a generic persistent volume resource that is provisioned/attached using an exec based plugin.", + "properties": { + "driver": { + "description": "driver is the name of the driver to use for this volume.", + "type": "string" + }, + "fsType": { + "description": "fsType is the Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + "type": "string" + }, + "options": { + "additionalProperties": { + "type": "string" + }, + "description": "options is Optional: this field holds extra command options if any.", + "type": "object" + }, + "readOnly": { + "description": "readOnly is Optional: defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "secretRef is Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.core.v1.FlexVolumeSource": { + "description": "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + "properties": { + "driver": { + "description": "driver is the name of the driver to use for this volume.", + "type": "string" + }, + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + "type": "string" + }, + "options": { + "additionalProperties": { + "type": "string" + }, + "description": "options is Optional: this field holds extra command options if any.", + "type": "object" + }, + "readOnly": { + "description": "readOnly is Optional: defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts." + } + }, + "required": [ + "driver" + ], + "type": "object" + }, + "io.k8s.api.core.v1.FlockerVolumeSource": { + "description": "Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling.", + "properties": { + "datasetName": { + "description": "datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated", + "type": "string" + }, + "datasetUUID": { + "description": "datasetUUID is the UUID of the dataset. This is unique identifier of a Flocker dataset", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.GCEPersistentDiskVolumeSource": { + "description": "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "type": "string" + }, + "partition": { + "description": "partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "format": "int32", + "type": "integer" + }, + "pdName": { + "description": "pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + "type": "boolean" + } + }, + "required": [ + "pdName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GRPCAction": { + "properties": { + "port": { + "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.", + "format": "int32", + "type": "integer" + }, + "service": { + "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.", + "type": "string" + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GitRepoVolumeSource": { + "description": "Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.\n\nDEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + "properties": { + "directory": { + "description": "directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.", + "type": "string" + }, + "repository": { + "description": "repository is the URL", + "type": "string" + }, + "revision": { + "description": "revision is the commit hash for the specified revision.", + "type": "string" + } + }, + "required": [ + "repository" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GlusterfsPersistentVolumeSource": { + "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "endpoints": { + "description": "endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "endpointsNamespace": { + "description": "endpointsNamespace is the namespace that contains Glusterfs endpoint. If this field is empty, the EndpointNamespace defaults to the same namespace as the bound PVC. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "path": { + "description": "path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "boolean" + } + }, + "required": [ + "endpoints", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.GlusterfsVolumeSource": { + "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + "properties": { + "endpoints": { + "description": "endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "path": { + "description": "path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "type": "boolean" + } + }, + "required": [ + "endpoints", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.HTTPGetAction": { + "description": "HTTPGetAction describes an action based on HTTP Get requests.", + "properties": { + "host": { + "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.", + "type": "string" + }, + "httpHeaders": { + "description": "Custom headers to set in the request. HTTP allows repeated headers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.HTTPHeader" + }, + "type": "array" + }, + "path": { + "description": "Path to access on the HTTP server.", + "type": "string" + }, + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME." + }, + "scheme": { + "description": "Scheme to use for connecting to the host. Defaults to HTTP.", + "type": "string" + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.HTTPHeader": { + "description": "HTTPHeader describes a custom header to be used in HTTP probes", + "properties": { + "name": { + "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.", + "type": "string" + }, + "value": { + "description": "The header field value", + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "type": "object" + }, + "io.k8s.api.core.v1.HostAlias": { + "description": "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.", + "properties": { + "hostnames": { + "description": "Hostnames for the above IP address.", + "items": { + "type": "string" + }, + "type": "array" + }, + "ip": { + "description": "IP address of the host file entry.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.HostIP": { + "description": "HostIP represents a single IP address allocated to the host.", + "properties": { + "ip": { + "description": "IP is the IP address assigned to the host", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.HostPathVolumeSource": { + "description": "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.", + "properties": { + "path": { + "description": "path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + "type": "string" + }, + "type": { + "description": "type for HostPath Volume Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ISCSIPersistentVolumeSource": { + "description": "ISCSIPersistentVolumeSource represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + "properties": { + "chapAuthDiscovery": { + "description": "chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication", + "type": "boolean" + }, + "chapAuthSession": { + "description": "chapAuthSession defines whether support iSCSI Session CHAP authentication", + "type": "boolean" + }, + "fsType": { + "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + "type": "string" + }, + "initiatorName": { + "description": "initiatorName is the custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + "type": "string" + }, + "iqn": { + "description": "iqn is Target iSCSI Qualified Name.", + "type": "string" + }, + "iscsiInterface": { + "description": "iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + "type": "string" + }, + "lun": { + "description": "lun is iSCSI Target Lun number.", + "format": "int32", + "type": "integer" + }, + "portals": { + "description": "portals is the iSCSI Target Portal List. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "items": { + "type": "string" + }, + "type": "array" + }, + "readOnly": { + "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "secretRef is the CHAP Secret for iSCSI target and initiator authentication" + }, + "targetPortal": { + "description": "targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "type": "string" + } + }, + "required": [ + "targetPortal", + "iqn", + "lun" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ISCSIVolumeSource": { + "description": "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + "properties": { + "chapAuthDiscovery": { + "description": "chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication", + "type": "boolean" + }, + "chapAuthSession": { + "description": "chapAuthSession defines whether support iSCSI Session CHAP authentication", + "type": "boolean" + }, + "fsType": { + "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + "type": "string" + }, + "initiatorName": { + "description": "initiatorName is the custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + "type": "string" + }, + "iqn": { + "description": "iqn is the target iSCSI Qualified Name.", + "type": "string" + }, + "iscsiInterface": { + "description": "iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + "type": "string" + }, + "lun": { + "description": "lun represents iSCSI Target Lun number.", + "format": "int32", + "type": "integer" + }, + "portals": { + "description": "portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "items": { + "type": "string" + }, + "type": "array" + }, + "readOnly": { + "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef is the CHAP Secret for iSCSI target and initiator authentication" + }, + "targetPortal": { + "description": "targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + "type": "string" + } + }, + "required": [ + "targetPortal", + "iqn", + "lun" + ], + "type": "object" + }, + "io.k8s.api.core.v1.KeyToPath": { + "description": "Maps a string key to a path within a volume.", + "properties": { + "key": { + "description": "key is the key to project.", + "type": "string" + }, + "mode": { + "description": "mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "path": { + "description": "path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.", + "type": "string" + } + }, + "required": [ + "key", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Lifecycle": { + "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.", + "properties": { + "postStart": { + "$ref": "#/definitions/io.k8s.api.core.v1.LifecycleHandler", + "description": "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks" + }, + "preStop": { + "$ref": "#/definitions/io.k8s.api.core.v1.LifecycleHandler", + "description": "PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The Pod's termination grace period countdown begins before the PreStop hook is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period (unless delayed by finalizers). Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LifecycleHandler": { + "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.", + "properties": { + "exec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ExecAction", + "description": "Exec specifies the action to take." + }, + "httpGet": { + "$ref": "#/definitions/io.k8s.api.core.v1.HTTPGetAction", + "description": "HTTPGet specifies the http request to perform." + }, + "tcpSocket": { + "$ref": "#/definitions/io.k8s.api.core.v1.TCPSocketAction", + "description": "Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. There are no validation of this field and lifecycle hooks will fail in runtime when tcp handler is specified." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LimitRange": { + "description": "LimitRange sets resource usage limits for each kind of resource in a Namespace.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LimitRange" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.LimitRangeSpec", + "description": "Spec defines the limits enforced. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "LimitRange", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.LimitRangeItem": { + "description": "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.", + "properties": { + "default": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Default resource requirement limit value by resource name if resource limit is omitted.", + "type": "object" + }, + "defaultRequest": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.", + "type": "object" + }, + "max": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Max usage constraints on this kind by resource name.", + "type": "object" + }, + "maxLimitRequestRatio": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.", + "type": "object" + }, + "min": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Min usage constraints on this kind by resource name.", + "type": "object" + }, + "type": { + "description": "Type of resource that this limit applies to.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.core.v1.LimitRangeList": { + "description": "LimitRangeList is a list of LimitRange items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of LimitRange objects. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LimitRange" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "LimitRangeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "LimitRangeList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.LimitRangeSpec": { + "description": "LimitRangeSpec defines a min/max usage limit for resources that match on kind.", + "properties": { + "limits": { + "description": "Limits is the list of LimitRangeItem objects that are enforced.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LimitRangeItem" + }, + "type": "array" + } + }, + "required": [ + "limits" + ], + "type": "object" + }, + "io.k8s.api.core.v1.LoadBalancerIngress": { + "description": "LoadBalancerIngress represents the status of a load-balancer ingress point: traffic intended for the service should be sent to an ingress point.", + "properties": { + "hostname": { + "description": "Hostname is set for load-balancer ingress points that are DNS based (typically AWS load-balancers)", + "type": "string" + }, + "ip": { + "description": "IP is set for load-balancer ingress points that are IP based (typically GCE or OpenStack load-balancers)", + "type": "string" + }, + "ports": { + "description": "Ports is a list of records of service ports If used, every port defined in the service should have an entry in it", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PortStatus" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LoadBalancerStatus": { + "description": "LoadBalancerStatus represents the status of a load-balancer.", + "properties": { + "ingress": { + "description": "Ingress is a list containing ingress points for the load-balancer. Traffic intended for the service should be sent to these ingress points.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LoadBalancerIngress" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.LocalObjectReference": { + "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.LocalVolumeSource": { + "description": "Local represents directly-attached storage with node affinity (Beta feature)", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. It applies only when the Path is a block device. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default value is to auto-select a filesystem if unspecified.", + "type": "string" + }, + "path": { + "description": "path of the full path to the volume on the node. It can be either a directory or block device (disk, partition, ...).", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NFSVolumeSource": { + "description": "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", + "properties": { + "path": { + "description": "path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + "type": "boolean" + }, + "server": { + "description": "server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + "type": "string" + } + }, + "required": [ + "server", + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Namespace": { + "description": "Namespace provides a scope for Names. Use of multiple namespaces is optional.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Namespace" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.NamespaceSpec", + "description": "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.NamespaceStatus", + "description": "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Namespace", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NamespaceCondition": { + "description": "NamespaceCondition contains details about state of namespace.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of namespace controller condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NamespaceList": { + "description": "NamespaceList is a list of Namespaces.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Namespace objects in the list. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Namespace" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NamespaceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "NamespaceList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NamespaceSpec": { + "description": "NamespaceSpec describes the attributes on a Namespace.", + "properties": { + "finalizers": { + "description": "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NamespaceStatus": { + "description": "NamespaceStatus is information about the current status of a Namespace.", + "properties": { + "conditions": { + "description": "Represents the latest available observations of a namespace's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NamespaceCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "phase": { + "description": "Phase is the current lifecycle phase of the namespace. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Node": { + "description": "Node is a worker node in Kubernetes. Each node will have a unique identifier in the cache (i.e. in etcd).", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Node" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSpec", + "description": "Spec defines the behavior of a node. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeStatus", + "description": "Most recently observed status of the node. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Node", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NodeAddress": { + "description": "NodeAddress contains information for the node's address.", + "properties": { + "address": { + "description": "The node address.", + "type": "string" + }, + "type": { + "description": "Node address type, one of Hostname, ExternalIP or InternalIP.", + "type": "string" + } + }, + "required": [ + "type", + "address" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeAffinity": { + "description": "Node affinity is a group of node affinity scheduling rules.", + "properties": { + "preferredDuringSchedulingIgnoredDuringExecution": { + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PreferredSchedulingTerm" + }, + "type": "array" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeCondition": { + "description": "NodeCondition contains condition information for a node.", + "properties": { + "lastHeartbeatTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time we got an update on a given condition." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transit from one status to another." + }, + "message": { + "description": "Human readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "(brief) reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of node condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeConfigSource": { + "description": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil. This API is deprecated since 1.22", + "properties": { + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapNodeConfigSource", + "description": "ConfigMap is a reference to a Node's ConfigMap" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeConfigStatus": { + "description": "NodeConfigStatus describes the status of the config assigned by Node.Spec.ConfigSource.", + "properties": { + "active": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "Active reports the checkpointed config the node is actively using. Active will represent either the current version of the Assigned config, or the current LastKnownGood config, depending on whether attempting to use the Assigned config results in an error." + }, + "assigned": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "Assigned reports the checkpointed config the node will try to use. When Node.Spec.ConfigSource is updated, the node checkpoints the associated config payload to local disk, along with a record indicating intended config. The node refers to this record to choose its config checkpoint, and reports this record in Assigned. Assigned only updates in the status after the record has been checkpointed to disk. When the Kubelet is restarted, it tries to make the Assigned config the Active config by loading and validating the checkpointed payload identified by Assigned." + }, + "error": { + "description": "Error describes any problems reconciling the Spec.ConfigSource to the Active config. Errors may occur, for example, attempting to checkpoint Spec.ConfigSource to the local Assigned record, attempting to checkpoint the payload associated with Spec.ConfigSource, attempting to load or validate the Assigned config, etc. Errors may occur at different points while syncing config. Earlier errors (e.g. download or checkpointing errors) will not result in a rollback to LastKnownGood, and may resolve across Kubelet retries. Later errors (e.g. loading or validating a checkpointed config) will result in a rollback to LastKnownGood. In the latter case, it is usually possible to resolve the error by fixing the config assigned in Spec.ConfigSource. You can find additional information for debugging by searching the error message in the Kubelet log. Error is a human-readable description of the error state; machines can check whether or not Error is empty, but should not rely on the stability of the Error text across Kubelet versions.", + "type": "string" + }, + "lastKnownGood": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "LastKnownGood reports the checkpointed config the node will fall back to when it encounters an error attempting to use the Assigned config. The Assigned config becomes the LastKnownGood config when the node determines that the Assigned config is stable and correct. This is currently implemented as a 10-minute soak period starting when the local record of Assigned config is updated. If the Assigned config is Active at the end of this period, it becomes the LastKnownGood. Note that if Spec.ConfigSource is reset to nil (use local defaults), the LastKnownGood is also immediately reset to nil, because the local default config is always assumed good. You should not make assumptions about the node's method of determining config stability and correctness, as this may change or become configurable in the future." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeDaemonEndpoints": { + "description": "NodeDaemonEndpoints lists ports opened by daemons running on the Node.", + "properties": { + "kubeletEndpoint": { + "$ref": "#/definitions/io.k8s.api.core.v1.DaemonEndpoint", + "description": "Endpoint on which Kubelet is listening." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeList": { + "description": "NodeList is the whole list of all Nodes which have been registered with master.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of nodes", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Node" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NodeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "NodeList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.NodeSelector": { + "description": "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.", + "properties": { + "nodeSelectorTerms": { + "description": "Required. A list of node selector terms. The terms are ORed.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorTerm" + }, + "type": "array" + } + }, + "required": [ + "nodeSelectorTerms" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.NodeSelectorRequirement": { + "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "The label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.", + "type": "string" + }, + "values": { + "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "io.k8s.api.core.v1.NodeSelectorTerm": { + "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", + "properties": { + "matchExpressions": { + "description": "A list of node selector requirements by node's labels.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorRequirement" + }, + "type": "array" + }, + "matchFields": { + "description": "A list of node selector requirements by node's fields.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorRequirement" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.NodeSpec": { + "description": "NodeSpec describes the attributes that a node is created with.", + "properties": { + "configSource": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource", + "description": "Deprecated: Previously used to specify the source of the node's configuration for the DynamicKubeletConfig feature. This feature is removed." + }, + "externalID": { + "description": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", + "type": "string" + }, + "podCIDR": { + "description": "PodCIDR represents the pod IP range assigned to the node.", + "type": "string" + }, + "podCIDRs": { + "description": "podCIDRs represents the IP ranges assigned to the node for usage by Pods on that node. If this field is specified, the 0th entry must match the podCIDR field. It may contain at most 1 value for each of IPv4 and IPv6.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-patch-strategy": "merge" + }, + "providerID": { + "description": "ID of the node assigned by the cloud provider in the format: ://", + "type": "string" + }, + "taints": { + "description": "If specified, the node's taints.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Taint" + }, + "type": "array" + }, + "unschedulable": { + "description": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeStatus": { + "description": "NodeStatus is information about the current status of a node.", + "properties": { + "addresses": { + "description": "List of addresses reachable to the node. Queried from cloud provider, if available. More info: https://kubernetes.io/docs/concepts/nodes/node/#addresses Note: This field is declared as mergeable, but the merge key is not sufficiently unique, which can cause data corruption when it is merged. Callers should instead use a full-replacement patch. See https://pr.k8s.io/79391 for an example. Consumers should assume that addresses can change during the lifetime of a Node. However, there are some exceptions where this may not be possible, such as Pods that inherit a Node's address in its own status or consumers of the downward API (status.hostIP).", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeAddress" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "allocatable": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Allocatable represents the resources of a node that are available for scheduling. Defaults to Capacity.", + "type": "object" + }, + "capacity": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Capacity represents the total resources of a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + "type": "object" + }, + "conditions": { + "description": "Conditions is an array of current observed node conditions. More info: https://kubernetes.io/docs/concepts/nodes/node/#condition", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "config": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigStatus", + "description": "Status of the config assigned to the node via the dynamic Kubelet config feature." + }, + "daemonEndpoints": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeDaemonEndpoints", + "description": "Endpoints of daemons running on the Node." + }, + "images": { + "description": "List of container images on this node", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerImage" + }, + "type": "array" + }, + "nodeInfo": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSystemInfo", + "description": "Set of ids/uuids to uniquely identify the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#info" + }, + "phase": { + "description": "NodePhase is the recently observed lifecycle phase of the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#phase The field is never populated, and now is deprecated.", + "type": "string" + }, + "volumesAttached": { + "description": "List of volumes that are attached to the node.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.AttachedVolume" + }, + "type": "array" + }, + "volumesInUse": { + "description": "List of attachable volumes in use (mounted) by the node.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.NodeSystemInfo": { + "description": "NodeSystemInfo is a set of ids/uuids to uniquely identify the node.", + "properties": { + "architecture": { + "description": "The Architecture reported by the node", + "type": "string" + }, + "bootID": { + "description": "Boot ID reported by the node.", + "type": "string" + }, + "containerRuntimeVersion": { + "description": "ContainerRuntime Version reported by the node through runtime remote API (e.g. containerd://1.4.2).", + "type": "string" + }, + "kernelVersion": { + "description": "Kernel Version reported by the node from 'uname -r' (e.g. 3.16.0-0.bpo.4-amd64).", + "type": "string" + }, + "kubeProxyVersion": { + "description": "KubeProxy Version reported by the node.", + "type": "string" + }, + "kubeletVersion": { + "description": "Kubelet Version reported by the node.", + "type": "string" + }, + "machineID": { + "description": "MachineID reported by the node. For unique machine identification in the cluster this field is preferred. Learn more from man(5) machine-id: http://man7.org/linux/man-pages/man5/machine-id.5.html", + "type": "string" + }, + "operatingSystem": { + "description": "The Operating System reported by the node", + "type": "string" + }, + "osImage": { + "description": "OS Image reported by the node from /etc/os-release (e.g. Debian GNU/Linux 7 (wheezy)).", + "type": "string" + }, + "systemUUID": { + "description": "SystemUUID reported by the node. For unique machine identification MachineID is preferred. This field is specific to Red Hat hosts https://access.redhat.com/documentation/en-us/red_hat_subscription_management/1/html/rhsm/uuid", + "type": "string" + } + }, + "required": [ + "machineID", + "systemUUID", + "bootID", + "kernelVersion", + "osImage", + "containerRuntimeVersion", + "kubeletVersion", + "kubeProxyVersion", + "operatingSystem", + "architecture" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ObjectFieldSelector": { + "description": "ObjectFieldSelector selects an APIVersioned field of an object.", + "properties": { + "apiVersion": { + "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".", + "type": "string" + }, + "fieldPath": { + "description": "Path of the field to select in the specified API version.", + "type": "string" + } + }, + "required": [ + "fieldPath" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.ObjectReference": { + "description": "ObjectReference contains enough information to let you inspect or modify the referred object.", + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string" + }, + "fieldPath": { + "description": "If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: \"spec.containers{name}\" (where \"name\" refers to the name of the container that triggered the event) or if no container name is specified \"spec.containers[2]\" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object.", + "type": "string" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "namespace": { + "description": "Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + "type": "string" + }, + "resourceVersion": { + "description": "Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "uid": { + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.PersistentVolume": { + "description": "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolume" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeSpec", + "description": "spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeStatus", + "description": "status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolume", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeClaim": { + "description": "PersistentVolumeClaim is a user's request for and claim to a persistent volume", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolumeClaim" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimSpec", + "description": "spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimStatus", + "description": "status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolumeClaim", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeClaimCondition": { + "description": "PersistentVolumeClaimCondition contains details about state of pvc", + "properties": { + "lastProbeTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastProbeTime is the time we probed the condition." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime is the time the condition transitioned from one status to another." + }, + "message": { + "description": "message is the human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "reason is a unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.", + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimList": { + "description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of persistent volume claims. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaim" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolumeClaimList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolumeClaimList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeClaimSpec": { + "description": "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", + "properties": { + "accessModes": { + "description": "accessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + "items": { + "type": "string" + }, + "type": "array" + }, + "dataSource": { + "$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference", + "description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef, and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified. If the namespace is specified, then dataSourceRef will not be copied to dataSource." + }, + "dataSourceRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.TypedObjectReference", + "description": "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the dataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, when namespace isn't specified in dataSourceRef, both fields (dataSource and dataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. When namespace is specified in dataSourceRef, dataSource isn't set to the same value and must be empty. There are three important differences between dataSource and dataSourceRef: * While dataSource only allows two specific types of objects, dataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled." + }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "selector is a label query over volumes to consider for binding." + }, + "storageClassName": { + "description": "storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", + "type": "string" + }, + "volumeMode": { + "description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.", + "type": "string" + }, + "volumeName": { + "description": "volumeName is the binding reference to the PersistentVolume backing this claim.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimStatus": { + "description": "PersistentVolumeClaimStatus is the current status of a persistent volume claim.", + "properties": { + "accessModes": { + "description": "accessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + "items": { + "type": "string" + }, + "type": "array" + }, + "allocatedResourceStatuses": { + "additionalProperties": { + "type": "string" + }, + "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "type": "object", + "x-kubernetes-map-type": "granular" + }, + "allocatedResources": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "type": "object" + }, + "capacity": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "capacity represents the actual resources of the underlying volume.", + "type": "object" + }, + "conditions": { + "description": "conditions is the current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "phase": { + "description": "phase represents the current phase of PersistentVolumeClaim.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimTemplate": { + "description": "PersistentVolumeClaimTemplate is used to produce PersistentVolumeClaim objects as part of an EphemeralVolumeSource.", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "May contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimSpec", + "description": "The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. The same fields as in a PersistentVolumeClaim are also valid here." + } + }, + "required": [ + "spec" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource": { + "description": "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).", + "properties": { + "claimName": { + "description": "claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + "type": "string" + }, + "readOnly": { + "description": "readOnly Will force the ReadOnly setting in VolumeMounts. Default false.", + "type": "boolean" + } + }, + "required": [ + "claimName" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeList": { + "description": "PersistentVolumeList is a list of PersistentVolume items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of persistent volumes. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolume" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PersistentVolumeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PersistentVolumeList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PersistentVolumeSpec": { + "description": "PersistentVolumeSpec is the specification of a persistent volume.", + "properties": { + "accessModes": { + "description": "accessModes contains all ways the volume can be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes", + "items": { + "type": "string" + }, + "type": "array" + }, + "awsElasticBlockStore": { + "$ref": "#/definitions/io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource", + "description": "awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore" + }, + "azureDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureDiskVolumeSource", + "description": "azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod." + }, + "azureFile": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureFilePersistentVolumeSource", + "description": "azureFile represents an Azure File Service mount on the host and bind mount to the pod." + }, + "capacity": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "capacity is the description of the persistent volume's resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + "type": "object" + }, + "cephfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.CephFSPersistentVolumeSource", + "description": "cephFS represents a Ceph FS mount on the host that shares a pod's lifetime" + }, + "cinder": { + "$ref": "#/definitions/io.k8s.api.core.v1.CinderPersistentVolumeSource", + "description": "cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md" + }, + "claimRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "claimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#binding", + "x-kubernetes-map-type": "granular" + }, + "csi": { + "$ref": "#/definitions/io.k8s.api.core.v1.CSIPersistentVolumeSource", + "description": "csi represents storage that is handled by an external CSI driver (Beta feature)." + }, + "fc": { + "$ref": "#/definitions/io.k8s.api.core.v1.FCVolumeSource", + "description": "fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod." + }, + "flexVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlexPersistentVolumeSource", + "description": "flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin." + }, + "flocker": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlockerVolumeSource", + "description": "flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running" + }, + "gcePersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.GCEPersistentDiskVolumeSource", + "description": "gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk" + }, + "glusterfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.GlusterfsPersistentVolumeSource", + "description": "glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md" + }, + "hostPath": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostPathVolumeSource", + "description": "hostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath" + }, + "iscsi": { + "$ref": "#/definitions/io.k8s.api.core.v1.ISCSIPersistentVolumeSource", + "description": "iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin." + }, + "local": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalVolumeSource", + "description": "local represents directly-attached storage with node affinity" + }, + "mountOptions": { + "description": "mountOptions is the list of mount options, e.g. [\"ro\", \"soft\"]. Not validated - mount will simply fail if one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options", + "items": { + "type": "string" + }, + "type": "array" + }, + "nfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.NFSVolumeSource", + "description": "nfs represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs" + }, + "nodeAffinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeNodeAffinity", + "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume." + }, + "persistentVolumeReclaimPolicy": { + "description": "persistentVolumeReclaimPolicy defines what happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", + "type": "string" + }, + "photonPersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource", + "description": "photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine" + }, + "portworxVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.PortworxVolumeSource", + "description": "portworxVolume represents a portworx volume attached and mounted on kubelets host machine" + }, + "quobyte": { + "$ref": "#/definitions/io.k8s.api.core.v1.QuobyteVolumeSource", + "description": "quobyte represents a Quobyte mount on the host that shares a pod's lifetime" + }, + "rbd": { + "$ref": "#/definitions/io.k8s.api.core.v1.RBDPersistentVolumeSource", + "description": "rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md" + }, + "scaleIO": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScaleIOPersistentVolumeSource", + "description": "scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes." + }, + "storageClassName": { + "description": "storageClassName is the name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.", + "type": "string" + }, + "storageos": { + "$ref": "#/definitions/io.k8s.api.core.v1.StorageOSPersistentVolumeSource", + "description": "storageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md" + }, + "volumeMode": { + "description": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec.", + "type": "string" + }, + "vsphereVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource", + "description": "vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PersistentVolumeStatus": { + "description": "PersistentVolumeStatus is the current status of a persistent volume.", + "properties": { + "lastPhaseTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature." + }, + "message": { + "description": "message is a human-readable message indicating details about why the volume is in this state.", + "type": "string" + }, + "phase": { + "description": "phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase", + "type": "string" + }, + "reason": { + "description": "reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource": { + "description": "Represents a Photon Controller persistent disk resource.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "pdID": { + "description": "pdID is the ID that identifies Photon Controller persistent disk", + "type": "string" + } + }, + "required": [ + "pdID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Pod": { + "description": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Pod" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSpec", + "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodStatus", + "description": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Pod", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodAffinity": { + "description": "Pod affinity is a group of inter pod affinity scheduling rules.", + "properties": { + "preferredDuringSchedulingIgnoredDuringExecution": { + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.WeightedPodAffinityTerm" + }, + "type": "array" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinityTerm" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodAffinityTerm": { + "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running", + "properties": { + "labelSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over a set of resources, in this case pods." + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means \"this pod's namespace\". An empty selector ({}) matches all namespaces." + }, + "namespaces": { + "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".", + "items": { + "type": "string" + }, + "type": "array" + }, + "topologyKey": { + "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.", + "type": "string" + } + }, + "required": [ + "topologyKey" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodAntiAffinity": { + "description": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", + "properties": { + "preferredDuringSchedulingIgnoredDuringExecution": { + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.WeightedPodAffinityTerm" + }, + "type": "array" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "description": "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinityTerm" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodCondition": { + "description": "PodCondition contains details for the current condition of this pod.", + "properties": { + "lastProbeTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time we probed the condition." + }, + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status is the status of the condition. Can be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + "type": "string" + }, + "type": { + "description": "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodDNSConfig": { + "description": "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.", + "properties": { + "nameservers": { + "description": "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "description": "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodDNSConfigOption" + }, + "type": "array" + }, + "searches": { + "description": "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodDNSConfigOption": { + "description": "PodDNSConfigOption defines DNS resolver options of a pod.", + "properties": { + "name": { + "description": "Required.", + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodIP": { + "description": "PodIP represents a single IP address allocated to the pod.", + "properties": { + "ip": { + "description": "IP is the IP address assigned to the pod", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodList": { + "description": "PodList is a list of Pods.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of pods. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Pod" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PodList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodOS": { + "description": "PodOS defines the OS parameters of a pod.", + "properties": { + "name": { + "description": "Name is the name of the operating system. The currently supported values are linux and windows. Additional value may be defined in future and can be one of: https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration Clients should expect to handle additional values and treat unrecognized values in this field as os: null", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodReadinessGate": { + "description": "PodReadinessGate contains the reference to a pod condition", + "properties": { + "conditionType": { + "description": "ConditionType refers to a condition in the pod's condition list with matching type.", + "type": "string" + } + }, + "required": [ + "conditionType" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodResourceClaim": { + "description": "PodResourceClaim references exactly one ResourceClaim through a ClaimSource. It adds a name to it that uniquely identifies the ResourceClaim inside the Pod. Containers that need access to the ResourceClaim reference it with this name.", + "properties": { + "name": { + "description": "Name uniquely identifies this resource claim inside the pod. This must be a DNS_LABEL.", + "type": "string" + }, + "source": { + "$ref": "#/definitions/io.k8s.api.core.v1.ClaimSource", + "description": "Source describes where to find the ResourceClaim." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodResourceClaimStatus": { + "description": "PodResourceClaimStatus is stored in the PodStatus for each PodResourceClaim which references a ResourceClaimTemplate. It stores the generated name for the corresponding ResourceClaim.", + "properties": { + "name": { + "description": "Name uniquely identifies this resource claim inside the pod. This must match the name of an entry in pod.spec.resourceClaims, which implies that the string must be a DNS_LABEL.", + "type": "string" + }, + "resourceClaimName": { + "description": "ResourceClaimName is the name of the ResourceClaim that was generated for the Pod in the namespace of the Pod. It this is unset, then generating a ResourceClaim was not necessary. The pod.spec.resourceClaims entry can be ignored in this case.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodSchedulingGate": { + "description": "PodSchedulingGate is associated to a Pod to guard its scheduling.", + "properties": { + "name": { + "description": "Name of the scheduling gate. Each scheduling gate must have a unique name field.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodSecurityContext": { + "description": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.", + "properties": { + "fsGroup": { + "description": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows.", + "format": "int64", + "type": "integer" + }, + "fsGroupChangePolicy": { + "description": "fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are \"OnRootMismatch\" and \"Always\". If not specified, \"Always\" is used. Note that this field cannot be set when spec.os.name is windows.", + "type": "string" + }, + "runAsGroup": { + "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.", + "format": "int64", + "type": "integer" + }, + "runAsNonRoot": { + "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "type": "boolean" + }, + "runAsUser": { + "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.", + "format": "int64", + "type": "integer" + }, + "seLinuxOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.SELinuxOptions", + "description": "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows." + }, + "seccompProfile": { + "$ref": "#/definitions/io.k8s.api.core.v1.SeccompProfile", + "description": "The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows." + }, + "supplementalGroups": { + "description": "A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for the uid of the container process. If unspecified, no additional groups are added to any container. Note that group memberships defined in the container image for the uid of the container process are still effective, even if they are not included in this list. Note that this field cannot be set when spec.os.name is windows.", + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + }, + "sysctls": { + "description": "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Sysctl" + }, + "type": "array" + }, + "windowsOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.WindowsSecurityContextOptions", + "description": "The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodSpec": { + "description": "PodSpec is a description of a pod.", + "properties": { + "activeDeadlineSeconds": { + "description": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.", + "format": "int64", + "type": "integer" + }, + "affinity": { + "$ref": "#/definitions/io.k8s.api.core.v1.Affinity", + "description": "If specified, the pod's scheduling constraints" + }, + "automountServiceAccountToken": { + "description": "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.", + "type": "boolean" + }, + "containers": { + "description": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "dnsConfig": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodDNSConfig", + "description": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy." + }, + "dnsPolicy": { + "description": "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.", + "type": "string" + }, + "enableServiceLinks": { + "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + "type": "boolean" + }, + "ephemeralContainers": { + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.EphemeralContainer" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "hostAliases": { + "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostAlias" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge" + }, + "hostIPC": { + "description": "Use the host's ipc namespace. Optional: Default to false.", + "type": "boolean" + }, + "hostNetwork": { + "description": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", + "type": "boolean" + }, + "hostPID": { + "description": "Use the host's pid namespace. Optional: Default to false.", + "type": "boolean" + }, + "hostUsers": { + "description": "Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.", + "type": "boolean" + }, + "hostname": { + "description": "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.", + "type": "string" + }, + "imagePullSecrets": { + "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "initContainers": { + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "nodeName": { + "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + "type": "string" + }, + "nodeSelector": { + "additionalProperties": { + "type": "string" + }, + "description": "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "os": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodOS", + "description": "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup" + }, + "overhead": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md", + "type": "object" + }, + "preemptionPolicy": { + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", + "type": "string" + }, + "priority": { + "description": "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", + "format": "int32", + "type": "integer" + }, + "priorityClassName": { + "description": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + "type": "string" + }, + "readinessGates": { + "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodReadinessGate" + }, + "type": "array" + }, + "resourceClaims": { + "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys" + }, + "restartPolicy": { + "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + "type": "string" + }, + "runtimeClassName": { + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class", + "type": "string" + }, + "schedulerName": { + "description": "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.", + "type": "string" + }, + "schedulingGates": { + "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSchedulingGate" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + }, + "securityContext": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSecurityContext", + "description": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field." + }, + "serviceAccount": { + "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + "type": "string" + }, + "serviceAccountName": { + "description": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + "type": "string" + }, + "setHostnameAsFQDN": { + "description": "If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.", + "type": "boolean" + }, + "shareProcessNamespace": { + "description": "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false.", + "type": "boolean" + }, + "subdomain": { + "description": "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.", + "type": "string" + }, + "terminationGracePeriodSeconds": { + "description": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.", + "format": "int64", + "type": "integer" + }, + "tolerations": { + "description": "If specified, the pod's tolerations.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Toleration" + }, + "type": "array" + }, + "topologySpreadConstraints": { + "description": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.TopologySpreadConstraint" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "topologyKey", + "whenUnsatisfiable" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "topologyKey", + "x-kubernetes-patch-strategy": "merge" + }, + "volumes": { + "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Volume" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys" + } + }, + "required": [ + "containers" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PodStatus": { + "description": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", + "properties": { + "conditions": { + "description": "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "containerStatuses": { + "description": "The list has one entry per container in the manifest. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus" + }, + "type": "array" + }, + "ephemeralContainerStatuses": { + "description": "Status for any ephemeral containers that have run in this pod.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus" + }, + "type": "array" + }, + "hostIP": { + "description": "hostIP holds the IP address of the host to which the pod is assigned. Empty if the pod has not started yet. A pod can be assigned to a node that has a problem in kubelet which in turns mean that HostIP will not be updated even if there is a node is assigned to pod", + "type": "string" + }, + "hostIPs": { + "description": "hostIPs holds the IP addresses allocated to the host. If this field is specified, the first entry must match the hostIP field. This list is empty if the pod has not started yet. A pod can be assigned to a node that has a problem in kubelet which in turns means that HostIPs will not be updated even if there is a node is assigned to this pod.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostIP" + }, + "type": "array", + "x-kubernetes-list-type": "atomic", + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge" + }, + "initContainerStatuses": { + "description": "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerStatus" + }, + "type": "array" + }, + "message": { + "description": "A human readable message indicating details about why the pod is in this condition.", + "type": "string" + }, + "nominatedNodeName": { + "description": "nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled.", + "type": "string" + }, + "phase": { + "description": "The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. There are five possible phase values:\n\nPending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. Succeeded: All containers in the pod have terminated in success, and will not be restarted. Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase", + "type": "string" + }, + "podIP": { + "description": "podIP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", + "type": "string" + }, + "podIPs": { + "description": "podIPs holds the IP addresses allocated to the pod. If this field is specified, the 0th entry must match the podIP field. Pods may be allocated at most 1 value for each of IPv4 and IPv6. This list is empty if no IPs have been allocated yet.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodIP" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge" + }, + "qosClass": { + "description": "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#quality-of-service-classes", + "type": "string" + }, + "reason": { + "description": "A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'", + "type": "string" + }, + "resize": { + "description": "Status of resources resize desired for pod's containers. It is empty if no resources resize is pending. Any changes to container resources will automatically set this to \"Proposed\"", + "type": "string" + }, + "resourceClaimStatuses": { + "description": "Status of resource claims.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaimStatus" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys" + }, + "startTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodTemplate": { + "description": "PodTemplate describes a template for creating copies of a predefined pod.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodTemplate" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template defines the pods that will be created from this pod template. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PodTemplate", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodTemplateList": { + "description": "PodTemplateList is a list of PodTemplates.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of pod templates", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplate" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodTemplateList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "PodTemplateList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.PodTemplateSpec": { + "description": "PodTemplateSpec describes the data a pod should have when created from a template", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSpec", + "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PortStatus": { + "properties": { + "error": { + "description": "Error is to record the problem with the service port The format of the error shall comply with the following rules: - built-in error values shall be specified in this file and those shall use\n CamelCase names\n- cloud provider specific error values must have names that comply with the\n format foo.example.com/CamelCase.", + "type": "string" + }, + "port": { + "description": "Port is the port number of the service port of which status is recorded here", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "Protocol is the protocol of the service port of which status is recorded here The supported values are: \"TCP\", \"UDP\", \"SCTP\"", + "type": "string" + } + }, + "required": [ + "port", + "protocol" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PortworxVolumeSource": { + "description": "PortworxVolumeSource represents a Portworx volume resource.", + "properties": { + "fsType": { + "description": "fSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "volumeID": { + "description": "volumeID uniquely identifies a Portworx volume", + "type": "string" + } + }, + "required": [ + "volumeID" + ], + "type": "object" + }, + "io.k8s.api.core.v1.PreferredSchedulingTerm": { + "description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).", + "properties": { + "preference": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelectorTerm", + "description": "A node selector term, associated with the corresponding weight." + }, + "weight": { + "description": "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "weight", + "preference" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Probe": { + "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.", + "properties": { + "exec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ExecAction", + "description": "Exec specifies the action to take." + }, + "failureThreshold": { + "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", + "format": "int32", + "type": "integer" + }, + "grpc": { + "$ref": "#/definitions/io.k8s.api.core.v1.GRPCAction", + "description": "GRPC specifies an action involving a GRPC port." + }, + "httpGet": { + "$ref": "#/definitions/io.k8s.api.core.v1.HTTPGetAction", + "description": "HTTPGet specifies the http request to perform." + }, + "initialDelaySeconds": { + "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "format": "int32", + "type": "integer" + }, + "periodSeconds": { + "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", + "format": "int32", + "type": "integer" + }, + "successThreshold": { + "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.", + "format": "int32", + "type": "integer" + }, + "tcpSocket": { + "$ref": "#/definitions/io.k8s.api.core.v1.TCPSocketAction", + "description": "TCPSocket specifies an action involving a TCP port." + }, + "terminationGracePeriodSeconds": { + "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.", + "format": "int64", + "type": "integer" + }, + "timeoutSeconds": { + "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ProjectedVolumeSource": { + "description": "Represents a projected volume source", + "properties": { + "defaultMode": { + "description": "defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "sources": { + "description": "sources is the list of volume projections", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.VolumeProjection" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.QuobyteVolumeSource": { + "description": "Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling.", + "properties": { + "group": { + "description": "group to map volume access to Default is no group", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.", + "type": "boolean" + }, + "registry": { + "description": "registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", + "type": "string" + }, + "tenant": { + "description": "tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin", + "type": "string" + }, + "user": { + "description": "user to map volume access to Defaults to serivceaccount user", + "type": "string" + }, + "volume": { + "description": "volume is a string that references an already created Quobyte volume by name.", + "type": "string" + } + }, + "required": [ + "registry", + "volume" + ], + "type": "object" + }, + "io.k8s.api.core.v1.RBDPersistentVolumeSource": { + "description": "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + "type": "string" + }, + "image": { + "description": "image is the rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "keyring": { + "description": "keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "monitors": { + "description": "monitors is a collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "pool": { + "description": "pool is the rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "secretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it" + }, + "user": { + "description": "user is the rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors", + "image" + ], + "type": "object" + }, + "io.k8s.api.core.v1.RBDVolumeSource": { + "description": "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + "type": "string" + }, + "image": { + "description": "image is the rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "keyring": { + "description": "keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "monitors": { + "description": "monitors is a collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "items": { + "type": "string" + }, + "type": "array" + }, + "pool": { + "description": "pool is the rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + }, + "readOnly": { + "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it" + }, + "user": { + "description": "user is the rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + "type": "string" + } + }, + "required": [ + "monitors", + "image" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ReplicationController": { + "description": "ReplicationController represents the configuration of a replication controller.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicationController" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationControllerSpec", + "description": "Spec defines the specification of the desired behavior of the replication controller. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationControllerStatus", + "description": "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ReplicationController", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ReplicationControllerCondition": { + "description": "ReplicationControllerCondition describes the state of a replication controller at a certain point.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "The last time the condition transitioned from one status to another." + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of replication controller condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ReplicationControllerList": { + "description": "ReplicationControllerList is a collection of replication controllers.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of replication controllers. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationController" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ReplicationControllerList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ReplicationControllerList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ReplicationControllerSpec": { + "description": "ReplicationControllerSpec is the specification of a replication controller.", + "properties": { + "minReadySeconds": { + "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec", + "description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. The only allowed template.spec.restartPolicy value is \"Always\". More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ReplicationControllerStatus": { + "description": "ReplicationControllerStatus represents the current status of a replication controller.", + "properties": { + "availableReplicas": { + "description": "The number of available replicas (ready for at least minReadySeconds) for this replication controller.", + "format": "int32", + "type": "integer" + }, + "conditions": { + "description": "Represents the latest available observations of a replication controller's current state.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ReplicationControllerCondition" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "fullyLabeledReplicas": { + "description": "The number of pods that have labels matching the labels of the pod template of the replication controller.", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "ObservedGeneration reflects the generation of the most recently observed replication controller.", + "format": "int64", + "type": "integer" + }, + "readyReplicas": { + "description": "The number of ready replicas for this replication controller.", + "format": "int32", + "type": "integer" + }, + "replicas": { + "description": "Replicas is the most recently observed number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "replicas" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ResourceClaim": { + "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.", + "properties": { + "name": { + "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ResourceFieldSelector": { + "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format", + "properties": { + "containerName": { + "description": "Container name: required for volumes, optional for env vars", + "type": "string" + }, + "divisor": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "Specifies the output format of the exposed resources, defaults to \"1\"" + }, + "resource": { + "description": "Required: resource to select", + "type": "string" + } + }, + "required": [ + "resource" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.ResourceQuota": { + "description": "ResourceQuota sets aggregate quota restrictions enforced per namespace", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceQuota" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceQuotaSpec", + "description": "Spec defines the desired quota. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceQuotaStatus", + "description": "Status defines the actual enforced quota and its current usage. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ResourceQuota", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ResourceQuotaList": { + "description": "ResourceQuotaList is a list of ResourceQuota items.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ResourceQuota objects. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceQuota" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceQuotaList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ResourceQuotaList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ResourceQuotaSpec": { + "description": "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", + "properties": { + "hard": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "hard is the set of desired hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + "type": "object" + }, + "scopeSelector": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScopeSelector", + "description": "scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota but expressed using ScopeSelectorOperator in combination with possible values. For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched." + }, + "scopes": { + "description": "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ResourceQuotaStatus": { + "description": "ResourceQuotaStatus defines the enforced hard limits and observed use.", + "properties": { + "hard": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Hard is the set of enforced hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + "type": "object" + }, + "used": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Used is the current observed total usage of the resource in the namespace.", + "type": "object" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ResourceRequirements": { + "description": "ResourceRequirements describes the compute resource requirements.", + "properties": { + "claims": { + "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceClaim" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + }, + "limits": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "type": "object" + }, + "requests": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/", + "type": "object" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SELinuxOptions": { + "description": "SELinuxOptions are the labels to be applied to the container", + "properties": { + "level": { + "description": "Level is SELinux level label that applies to the container.", + "type": "string" + }, + "role": { + "description": "Role is a SELinux role label that applies to the container.", + "type": "string" + }, + "type": { + "description": "Type is a SELinux type label that applies to the container.", + "type": "string" + }, + "user": { + "description": "User is a SELinux user label that applies to the container.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ScaleIOPersistentVolumeSource": { + "description": "ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\"", + "type": "string" + }, + "gateway": { + "description": "gateway is the host address of the ScaleIO API Gateway.", + "type": "string" + }, + "protectionDomain": { + "description": "protectionDomain is the name of the ScaleIO Protection Domain for the configured storage.", + "type": "string" + }, + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretReference", + "description": "secretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail." + }, + "sslEnabled": { + "description": "sslEnabled is the flag to enable/disable SSL communication with Gateway, default false", + "type": "boolean" + }, + "storageMode": { + "description": "storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + "type": "string" + }, + "storagePool": { + "description": "storagePool is the ScaleIO Storage Pool associated with the protection domain.", + "type": "string" + }, + "system": { + "description": "system is the name of the storage system as configured in ScaleIO.", + "type": "string" + }, + "volumeName": { + "description": "volumeName is the name of a volume already created in the ScaleIO system that is associated with this volume source.", + "type": "string" + } + }, + "required": [ + "gateway", + "system", + "secretRef" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ScaleIOVolumeSource": { + "description": "ScaleIOVolumeSource represents a persistent ScaleIO volume", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\".", + "type": "string" + }, + "gateway": { + "description": "gateway is the host address of the ScaleIO API Gateway.", + "type": "string" + }, + "protectionDomain": { + "description": "protectionDomain is the name of the ScaleIO Protection Domain for the configured storage.", + "type": "string" + }, + "readOnly": { + "description": "readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail." + }, + "sslEnabled": { + "description": "sslEnabled Flag enable/disable SSL communication with Gateway, default false", + "type": "boolean" + }, + "storageMode": { + "description": "storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + "type": "string" + }, + "storagePool": { + "description": "storagePool is the ScaleIO Storage Pool associated with the protection domain.", + "type": "string" + }, + "system": { + "description": "system is the name of the storage system as configured in ScaleIO.", + "type": "string" + }, + "volumeName": { + "description": "volumeName is the name of a volume already created in the ScaleIO system that is associated with this volume source.", + "type": "string" + } + }, + "required": [ + "gateway", + "system", + "secretRef" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ScopeSelector": { + "description": "A scope selector represents the AND of the selectors represented by the scoped-resource selector requirements.", + "properties": { + "matchExpressions": { + "description": "A list of scope selector requirements by scope of the resources.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScopedResourceSelectorRequirement" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.ScopedResourceSelectorRequirement": { + "description": "A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator that relates the scope name and values.", + "properties": { + "operator": { + "description": "Represents a scope's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist.", + "type": "string" + }, + "scopeName": { + "description": "The name of the scope that the selector applies to.", + "type": "string" + }, + "values": { + "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "scopeName", + "operator" + ], + "type": "object" + }, + "io.k8s.api.core.v1.SeccompProfile": { + "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.", + "properties": { + "localhostProfile": { + "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.", + "type": "string" + }, + "type": { + "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "localhostProfile": "LocalhostProfile" + } + } + ] + }, + "io.k8s.api.core.v1.Secret": { + "description": "Secret holds secret data of a certain type. The total bytes of the values in the Data field must be less than MaxSecretSize bytes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "data": { + "additionalProperties": { + "format": "byte", + "type": "string" + }, + "description": "Data contains the secret data. Each key must consist of alphanumeric characters, '-', '_' or '.'. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4", + "type": "object" + }, + "immutable": { + "description": "Immutable, if set to true, ensures that data stored in the Secret cannot be updated (only object metadata can be modified). If not set to true, the field can be modified at any time. Defaulted to nil.", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Secret" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "stringData": { + "additionalProperties": { + "type": "string" + }, + "description": "stringData allows specifying non-binary secret data in string form. It is provided as a write-only input field for convenience. All keys and values are merged into the data field on write, overwriting any existing values. The stringData field is never output when reading from the API.", + "type": "object" + }, + "type": { + "description": "Used to facilitate programmatic handling of secret data. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Secret", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.SecretEnvSource": { + "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.", + "properties": { + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the Secret must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecretKeySelector": { + "description": "SecretKeySelector selects a key of a Secret.", + "properties": { + "key": { + "description": "The key of the secret to select from. Must be a valid secret key.", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "Specify whether the Secret or its key must be defined", + "type": "boolean" + } + }, + "required": [ + "key" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.SecretList": { + "description": "SecretList is a list of Secret.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of secret objects. More info: https://kubernetes.io/docs/concepts/configuration/secret", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Secret" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "SecretList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "SecretList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.SecretProjection": { + "description": "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.", + "properties": { + "items": { + "description": "items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "optional": { + "description": "optional field specify whether the Secret or its key must be defined", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecretReference": { + "description": "SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace", + "properties": { + "name": { + "description": "name is unique within a namespace to reference a secret resource.", + "type": "string" + }, + "namespace": { + "description": "namespace defines the space within which the secret name must be unique.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.SecretVolumeSource": { + "description": "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.", + "properties": { + "defaultMode": { + "description": "defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + "format": "int32", + "type": "integer" + }, + "items": { + "description": "items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.KeyToPath" + }, + "type": "array" + }, + "optional": { + "description": "optional field specify whether the Secret or its keys must be defined", + "type": "boolean" + }, + "secretName": { + "description": "secretName is the name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SecurityContext": { + "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", + "properties": { + "allowPrivilegeEscalation": { + "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.", + "type": "boolean" + }, + "capabilities": { + "$ref": "#/definitions/io.k8s.api.core.v1.Capabilities", + "description": "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows." + }, + "privileged": { + "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.", + "type": "boolean" + }, + "procMount": { + "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.", + "type": "string" + }, + "readOnlyRootFilesystem": { + "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.", + "type": "boolean" + }, + "runAsGroup": { + "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.", + "format": "int64", + "type": "integer" + }, + "runAsNonRoot": { + "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "type": "boolean" + }, + "runAsUser": { + "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.", + "format": "int64", + "type": "integer" + }, + "seLinuxOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.SELinuxOptions", + "description": "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows." + }, + "seccompProfile": { + "$ref": "#/definitions/io.k8s.api.core.v1.SeccompProfile", + "description": "The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows." + }, + "windowsOptions": { + "$ref": "#/definitions/io.k8s.api.core.v1.WindowsSecurityContextOptions", + "description": "The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Service": { + "description": "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Service" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceSpec", + "description": "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceStatus", + "description": "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Service", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServiceAccount": { + "description": "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "automountServiceAccountToken": { + "description": "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level.", + "type": "boolean" + }, + "imagePullSecrets": { + "description": "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ServiceAccount" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "secrets": { + "description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ServiceAccount", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServiceAccountList": { + "description": "ServiceAccountList is a list of ServiceAccount objects", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ServiceAccounts. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceAccount" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ServiceAccountList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ServiceAccountList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServiceAccountTokenProjection": { + "description": "ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise).", + "properties": { + "audience": { + "description": "audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.", + "type": "string" + }, + "expirationSeconds": { + "description": "expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.", + "format": "int64", + "type": "integer" + }, + "path": { + "description": "path is the path relative to the mount point of the file to project the token into.", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ServiceList": { + "description": "ServiceList holds a list of services.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of services", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Service" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ServiceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "ServiceList", + "version": "v1" + } + ] + }, + "io.k8s.api.core.v1.ServicePort": { + "description": "ServicePort contains information on service's port.", + "properties": { + "appProtocol": { + "description": "The application protocol for this port. This is used as a hint for implementations to offer richer behavior for protocols that they understand. This field follows standard Kubernetes label syntax. Valid values are either:\n\n* Un-prefixed protocol names - reserved for IANA standard service names (as per RFC-6335 and https://www.iana.org/assignments/service-names).\n\n* Kubernetes-defined prefixed names:\n * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455\n * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455\n\n* Other protocols should use implementation-defined prefixed names such as mycompany.com/my-custom-protocol.", + "type": "string" + }, + "name": { + "description": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.", + "type": "string" + }, + "nodePort": { + "description": "The port on each node on which this service is exposed when type is NodePort or LoadBalancer. Usually assigned by the system. If a value is specified, in-range, and not in use it will be used, otherwise the operation will fail. If not specified, a port will be allocated if this Service requires one. If this field is specified when creating a Service which does not need it, creation will fail. This field will be wiped when updating a Service to no longer need it (e.g. changing type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport", + "format": "int32", + "type": "integer" + }, + "port": { + "description": "The port that will be exposed by this service.", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.", + "type": "string" + }, + "targetPort": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service" + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.ServiceSpec": { + "description": "ServiceSpec describes the attributes that a user creates on a service.", + "properties": { + "allocateLoadBalancerNodePorts": { + "description": "allocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for services with type LoadBalancer. Default is \"true\". It may be set to \"false\" if the cluster load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a value), those requests will be respected, regardless of this field. This field may only be set for services with type LoadBalancer and will be cleared if the type is changed to any other type.", + "type": "boolean" + }, + "clusterIP": { + "description": "clusterIP is the IP address of the service and is usually assigned randomly. If an address is specified manually, is in-range (as per system configuration), and is not in use, it will be allocated to the service; otherwise creation of the service will fail. This field may not be changed through updates unless the type field is also being changed to ExternalName (which requires this field to be blank) or the type field is being changed from ExternalName (in which case this field may optionally be specified, as describe above). Valid values are \"None\", empty string (\"\"), or a valid IP address. Setting this to \"None\" makes a \"headless service\" (no virtual IP), which is useful when direct endpoint connections are preferred and proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. If this field is specified when creating a Service of type ExternalName, creation will fail. This field will be wiped when updating a Service to type ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "type": "string" + }, + "clusterIPs": { + "description": "ClusterIPs is a list of IP addresses assigned to this service, and are usually assigned randomly. If an address is specified manually, is in-range (as per system configuration), and is not in use, it will be allocated to the service; otherwise creation of the service will fail. This field may not be changed through updates unless the type field is also being changed to ExternalName (which requires this field to be empty) or the type field is being changed from ExternalName (in which case this field may optionally be specified, as describe above). Valid values are \"None\", empty string (\"\"), or a valid IP address. Setting this to \"None\" makes a \"headless service\" (no virtual IP), which is useful when direct endpoint connections are preferred and proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. If this field is specified when creating a Service of type ExternalName, creation will fail. This field will be wiped when updating a Service to type ExternalName. If this field is not specified, it will be initialized from the clusterIP field. If this field is specified, clients must ensure that clusterIPs[0] and clusterIP have the same value.\n\nThis field may hold a maximum of two entries (dual-stack IPs, in either order). These IPs must correspond to the values of the ipFamilies field. Both clusterIPs and ipFamilies are governed by the ipFamilyPolicy field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "externalIPs": { + "description": "externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.", + "items": { + "type": "string" + }, + "type": "array" + }, + "externalName": { + "description": "externalName is the external reference that discovery mechanisms will return as an alias for this service (e.g. a DNS CNAME record). No proxying will be involved. Must be a lowercase RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires `type` to be \"ExternalName\".", + "type": "string" + }, + "externalTrafficPolicy": { + "description": "externalTrafficPolicy describes how nodes distribute service traffic they receive on one of the Service's \"externally-facing\" addresses (NodePorts, ExternalIPs, and LoadBalancer IPs). If set to \"Local\", the proxy will configure the service in a way that assumes that external load balancers will take care of balancing the service traffic between nodes, and so each node will deliver traffic only to the node-local endpoints of the service, without masquerading the client source IP. (Traffic mistakenly sent to a node with no endpoints will be dropped.) The default value, \"Cluster\", uses the standard behavior of routing to all endpoints evenly (possibly modified by topology and other features). Note that traffic sent to an External IP or LoadBalancer IP from within the cluster will always get \"Cluster\" semantics, but clients sending to a NodePort from within the cluster may need to take traffic policy into account when picking a node.", + "type": "string" + }, + "healthCheckNodePort": { + "description": "healthCheckNodePort specifies the healthcheck nodePort for the service. This only applies when type is set to LoadBalancer and externalTrafficPolicy is set to Local. If a value is specified, is in-range, and is not in use, it will be used. If not specified, a value will be automatically allocated. External systems (e.g. load-balancers) can use this port to determine if a given node holds endpoints for this service or not. If this field is specified when creating a Service which does not need it, creation will fail. This field will be wiped when updating a Service to no longer need it (e.g. changing type). This field cannot be updated once set.", + "format": "int32", + "type": "integer" + }, + "internalTrafficPolicy": { + "description": "InternalTrafficPolicy describes how nodes distribute service traffic they receive on the ClusterIP. If set to \"Local\", the proxy will assume that pods only want to talk to endpoints of the service on the same node as the pod, dropping the traffic if there are no local endpoints. The default value, \"Cluster\", uses the standard behavior of routing to all endpoints evenly (possibly modified by topology and other features).", + "type": "string" + }, + "ipFamilies": { + "description": "IPFamilies is a list of IP families (e.g. IPv4, IPv6) assigned to this service. This field is usually assigned automatically based on cluster configuration and the ipFamilyPolicy field. If this field is specified manually, the requested family is available in the cluster, and ipFamilyPolicy allows it, it will be used; otherwise creation of the service will fail. This field is conditionally mutable: it allows for adding or removing a secondary IP family, but it does not allow changing the primary IP family of the Service. Valid values are \"IPv4\" and \"IPv6\". This field only applies to Services of types ClusterIP, NodePort, and LoadBalancer, and does apply to \"headless\" services. This field will be wiped when updating a Service to type ExternalName.\n\nThis field may hold a maximum of two entries (dual-stack families, in either order). These families must correspond to the values of the clusterIPs field, if specified. Both clusterIPs and ipFamilies are governed by the ipFamilyPolicy field.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "ipFamilyPolicy": { + "description": "IPFamilyPolicy represents the dual-stack-ness requested or required by this Service. If there is no value provided, then this field will be set to SingleStack. Services can be \"SingleStack\" (a single IP family), \"PreferDualStack\" (two IP families on dual-stack configured clusters or a single IP family on single-stack clusters), or \"RequireDualStack\" (two IP families on dual-stack configured clusters, otherwise fail). The ipFamilies and clusterIPs fields depend on the value of this field. This field will be wiped when updating a service to type ExternalName.", + "type": "string" + }, + "loadBalancerClass": { + "description": "loadBalancerClass is the class of the load balancer implementation this Service belongs to. If specified, the value of this field must be a label-style identifier, with an optional prefix, e.g. \"internal-vip\" or \"example.com/internal-vip\". Unprefixed names are reserved for end-users. This field can only be set when the Service type is 'LoadBalancer'. If not set, the default load balancer implementation is used, today this is typically done through the cloud provider integration, but should apply for any default implementation. If set, it is assumed that a load balancer implementation is watching for Services with a matching class. Any default load balancer implementation (e.g. cloud providers) should ignore Services that set this field. This field can only be set when creating or updating a Service to type 'LoadBalancer'. Once set, it can not be changed. This field will be wiped when a service is updated to a non 'LoadBalancer' type.", + "type": "string" + }, + "loadBalancerIP": { + "description": "Only applies to Service Type: LoadBalancer. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature. Deprecated: This field was under-specified and its meaning varies across implementations. Using it is non-portable and it may not support dual-stack. Users are encouraged to use implementation-specific annotations when available.", + "type": "string" + }, + "loadBalancerSourceRanges": { + "description": "If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/", + "items": { + "type": "string" + }, + "type": "array" + }, + "ports": { + "description": "The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServicePort" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "port", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "port", + "x-kubernetes-patch-strategy": "merge" + }, + "publishNotReadyAddresses": { + "description": "publishNotReadyAddresses indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready. The primary use case for setting this field is for a StatefulSet's Headless Service to propagate SRV DNS records for its Pods for the purpose of peer discovery. The Kubernetes controllers that generate Endpoints and EndpointSlice resources for Services interpret this to mean that all endpoints are considered \"ready\" even if the Pods themselves are not. Agents which consume only Kubernetes generated endpoints through the Endpoints or EndpointSlice resources can safely assume this behavior.", + "type": "boolean" + }, + "selector": { + "additionalProperties": { + "type": "string" + }, + "description": "Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/", + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "sessionAffinity": { + "description": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + "type": "string" + }, + "sessionAffinityConfig": { + "$ref": "#/definitions/io.k8s.api.core.v1.SessionAffinityConfig", + "description": "sessionAffinityConfig contains the configurations of session affinity." + }, + "type": { + "description": "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object or EndpointSlice objects. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a virtual IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the same endpoints as the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the same endpoints as the clusterIP. \"ExternalName\" aliases this service to the specified externalName. Several other fields do not apply to ExternalName services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ServiceStatus": { + "description": "ServiceStatus represents the current status of a service.", + "properties": { + "conditions": { + "description": "Current service state", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "loadBalancer": { + "$ref": "#/definitions/io.k8s.api.core.v1.LoadBalancerStatus", + "description": "LoadBalancer contains the current status of the load-balancer, if one is present." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.SessionAffinityConfig": { + "description": "SessionAffinityConfig represents the configurations of session affinity.", + "properties": { + "clientIP": { + "$ref": "#/definitions/io.k8s.api.core.v1.ClientIPConfig", + "description": "clientIP contains the configurations of Client IP based session affinity." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.StorageOSPersistentVolumeSource": { + "description": "Represents a StorageOS persistent volume resource.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "secretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted." + }, + "volumeName": { + "description": "volumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + "type": "string" + }, + "volumeNamespace": { + "description": "volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.StorageOSVolumeSource": { + "description": "Represents a StorageOS persistent volume resource.", + "properties": { + "fsType": { + "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "readOnly": { + "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + "type": "boolean" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference", + "description": "secretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted." + }, + "volumeName": { + "description": "volumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + "type": "string" + }, + "volumeNamespace": { + "description": "volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Sysctl": { + "description": "Sysctl defines a kernel parameter to be set", + "properties": { + "name": { + "description": "Name of a property to set", + "type": "string" + }, + "value": { + "description": "Value of a property to set", + "type": "string" + } + }, + "required": [ + "name", + "value" + ], + "type": "object" + }, + "io.k8s.api.core.v1.TCPSocketAction": { + "description": "TCPSocketAction describes an action based on opening a socket", + "properties": { + "host": { + "description": "Optional: Host name to connect to, defaults to the pod IP.", + "type": "string" + }, + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME." + } + }, + "required": [ + "port" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Taint": { + "description": "The node this Taint is attached to has the \"effect\" on any pod that does not tolerate the Taint.", + "properties": { + "effect": { + "description": "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute.", + "type": "string" + }, + "key": { + "description": "Required. The taint key to be applied to a node.", + "type": "string" + }, + "timeAdded": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints." + }, + "value": { + "description": "The taint value corresponding to the taint key.", + "type": "string" + } + }, + "required": [ + "key", + "effect" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Toleration": { + "description": "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .", + "properties": { + "effect": { + "description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.", + "type": "string" + }, + "key": { + "description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.", + "type": "string" + }, + "operator": { + "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "type": "string" + }, + "tolerationSeconds": { + "description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.", + "format": "int64", + "type": "integer" + }, + "value": { + "description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.TopologySelectorLabelRequirement": { + "description": "A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.", + "properties": { + "key": { + "description": "The label key that the selector applies to.", + "type": "string" + }, + "values": { + "description": "An array of string values. One value must match the label to be selected. Each entry in Values is ORed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "key", + "values" + ], + "type": "object" + }, + "io.k8s.api.core.v1.TopologySelectorTerm": { + "description": "A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.", + "properties": { + "matchLabelExpressions": { + "description": "A list of topology selector requirements by labels.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.TopologySelectorLabelRequirement" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.TopologySpreadConstraint": { + "description": "TopologySpreadConstraint specifies how to spread matching pods among the given topology.", + "properties": { + "labelSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain." + }, + "matchLabelKeys": { + "description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.\n\nThis is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "maxSkew": { + "description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.", + "format": "int32", + "type": "integer" + }, + "minDomains": { + "description": "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.\n\nFor example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.\n\nThis is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default).", + "format": "int32", + "type": "integer" + }, + "nodeAffinityPolicy": { + "description": "NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.\n\nIf this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.", + "type": "string" + }, + "nodeTaintsPolicy": { + "description": "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.\n\nIf this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.", + "type": "string" + }, + "topologyKey": { + "description": "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a \"bucket\", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is \"kubernetes.io/hostname\", each Node is a domain of that topology. And, if TopologyKey is \"topology.kubernetes.io/zone\", each zone is a domain of that topology. It's a required field.", + "type": "string" + }, + "whenUnsatisfiable": { + "description": "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,\n but giving higher precedence to topologies that would help reduce the\n skew.\nA constraint is considered \"Unsatisfiable\" for an incoming pod if and only if every possible node assignment for that pod would violate \"MaxSkew\" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field.", + "type": "string" + } + }, + "required": [ + "maxSkew", + "topologyKey", + "whenUnsatisfiable" + ], + "type": "object" + }, + "io.k8s.api.core.v1.TypedLocalObjectReference": { + "description": "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.core.v1.TypedObjectReference": { + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.Volume": { + "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", + "properties": { + "awsElasticBlockStore": { + "$ref": "#/definitions/io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource", + "description": "awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore" + }, + "azureDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureDiskVolumeSource", + "description": "azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod." + }, + "azureFile": { + "$ref": "#/definitions/io.k8s.api.core.v1.AzureFileVolumeSource", + "description": "azureFile represents an Azure File Service mount on the host and bind mount to the pod." + }, + "cephfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.CephFSVolumeSource", + "description": "cephFS represents a Ceph FS mount on the host that shares a pod's lifetime" + }, + "cinder": { + "$ref": "#/definitions/io.k8s.api.core.v1.CinderVolumeSource", + "description": "cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md" + }, + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapVolumeSource", + "description": "configMap represents a configMap that should populate this volume" + }, + "csi": { + "$ref": "#/definitions/io.k8s.api.core.v1.CSIVolumeSource", + "description": "csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature)." + }, + "downwardAPI": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeSource", + "description": "downwardAPI represents downward API about the pod that should populate this volume" + }, + "emptyDir": { + "$ref": "#/definitions/io.k8s.api.core.v1.EmptyDirVolumeSource", + "description": "emptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir" + }, + "ephemeral": { + "$ref": "#/definitions/io.k8s.api.core.v1.EphemeralVolumeSource", + "description": "ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed.\n\nUse this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity\n tracking are needed,\nc) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through\n a PersistentVolumeClaim (see EphemeralVolumeSource for more\n information on the connection between this volume type\n and PersistentVolumeClaim).\n\nUse PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod.\n\nUse CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information.\n\nA pod can use both types of ephemeral volumes and persistent volumes at the same time." + }, + "fc": { + "$ref": "#/definitions/io.k8s.api.core.v1.FCVolumeSource", + "description": "fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod." + }, + "flexVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlexVolumeSource", + "description": "flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin." + }, + "flocker": { + "$ref": "#/definitions/io.k8s.api.core.v1.FlockerVolumeSource", + "description": "flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running" + }, + "gcePersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.GCEPersistentDiskVolumeSource", + "description": "gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk" + }, + "gitRepo": { + "$ref": "#/definitions/io.k8s.api.core.v1.GitRepoVolumeSource", + "description": "gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container." + }, + "glusterfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.GlusterfsVolumeSource", + "description": "glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md" + }, + "hostPath": { + "$ref": "#/definitions/io.k8s.api.core.v1.HostPathVolumeSource", + "description": "hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath" + }, + "iscsi": { + "$ref": "#/definitions/io.k8s.api.core.v1.ISCSIVolumeSource", + "description": "iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md" + }, + "name": { + "description": "name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + "type": "string" + }, + "nfs": { + "$ref": "#/definitions/io.k8s.api.core.v1.NFSVolumeSource", + "description": "nfs represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs" + }, + "persistentVolumeClaim": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource", + "description": "persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims" + }, + "photonPersistentDisk": { + "$ref": "#/definitions/io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource", + "description": "photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine" + }, + "portworxVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.PortworxVolumeSource", + "description": "portworxVolume represents a portworx volume attached and mounted on kubelets host machine" + }, + "projected": { + "$ref": "#/definitions/io.k8s.api.core.v1.ProjectedVolumeSource", + "description": "projected items for all in one resources secrets, configmaps, and downward API" + }, + "quobyte": { + "$ref": "#/definitions/io.k8s.api.core.v1.QuobyteVolumeSource", + "description": "quobyte represents a Quobyte mount on the host that shares a pod's lifetime" + }, + "rbd": { + "$ref": "#/definitions/io.k8s.api.core.v1.RBDVolumeSource", + "description": "rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md" + }, + "scaleIO": { + "$ref": "#/definitions/io.k8s.api.core.v1.ScaleIOVolumeSource", + "description": "scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes." + }, + "secret": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretVolumeSource", + "description": "secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret" + }, + "storageos": { + "$ref": "#/definitions/io.k8s.api.core.v1.StorageOSVolumeSource", + "description": "storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes." + }, + "vsphereVolume": { + "$ref": "#/definitions/io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource", + "description": "vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.core.v1.VolumeDevice": { + "description": "volumeDevice describes a mapping of a raw block device within a container.", + "properties": { + "devicePath": { + "description": "devicePath is the path inside of the container that the device will be mapped to.", + "type": "string" + }, + "name": { + "description": "name must match the name of a persistentVolumeClaim in the pod", + "type": "string" + } + }, + "required": [ + "name", + "devicePath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.VolumeMount": { + "description": "VolumeMount describes a mounting of a Volume within a container.", + "properties": { + "mountPath": { + "description": "Path within the container at which the volume should be mounted. Must not contain ':'.", + "type": "string" + }, + "mountPropagation": { + "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.", + "type": "string" + }, + "name": { + "description": "This must match the Name of a Volume.", + "type": "string" + }, + "readOnly": { + "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.", + "type": "boolean" + }, + "subPath": { + "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).", + "type": "string" + }, + "subPathExpr": { + "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.", + "type": "string" + } + }, + "required": [ + "name", + "mountPath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.VolumeNodeAffinity": { + "description": "VolumeNodeAffinity defines constraints that limit what nodes this volume can be accessed from.", + "properties": { + "required": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "required specifies hard node constraints that must be met." + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.VolumeProjection": { + "description": "Projection that may be projected along with other supported volume types", + "properties": { + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapProjection", + "description": "configMap information about the configMap data to project" + }, + "downwardAPI": { + "$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIProjection", + "description": "downwardAPI information about the downwardAPI data to project" + }, + "secret": { + "$ref": "#/definitions/io.k8s.api.core.v1.SecretProjection", + "description": "secret information about the secret data to project" + }, + "serviceAccountToken": { + "$ref": "#/definitions/io.k8s.api.core.v1.ServiceAccountTokenProjection", + "description": "serviceAccountToken is information about the serviceAccountToken data to project" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource": { + "description": "Represents a vSphere volume resource.", + "properties": { + "fsType": { + "description": "fsType is filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + "type": "string" + }, + "storagePolicyID": { + "description": "storagePolicyID is the storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.", + "type": "string" + }, + "storagePolicyName": { + "description": "storagePolicyName is the storage Policy Based Management (SPBM) profile name.", + "type": "string" + }, + "volumePath": { + "description": "volumePath is the path that identifies vSphere volume vmdk", + "type": "string" + } + }, + "required": [ + "volumePath" + ], + "type": "object" + }, + "io.k8s.api.core.v1.WeightedPodAffinityTerm": { + "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", + "properties": { + "podAffinityTerm": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodAffinityTerm", + "description": "Required. A pod affinity term, associated with the corresponding weight." + }, + "weight": { + "description": "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "weight", + "podAffinityTerm" + ], + "type": "object" + }, + "io.k8s.api.core.v1.WindowsSecurityContextOptions": { + "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.", + "properties": { + "gmsaCredentialSpec": { + "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.", + "type": "string" + }, + "gmsaCredentialSpecName": { + "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.", + "type": "string" + }, + "hostProcess": { + "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.", + "type": "boolean" + }, + "runAsUserName": { + "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.discovery.v1.Endpoint": { + "description": "Endpoint represents a single logical \"backend\" implementing a service.", + "properties": { + "addresses": { + "description": "addresses of this endpoint. The contents of this field are interpreted according to the corresponding EndpointSlice addressType field. Consumers must handle different types of addresses in the context of their own capabilities. This must contain at least one address but no more than 100. These are all assumed to be fungible and clients may choose to only use the first element. Refer to: https://issue.k8s.io/106267", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "conditions": { + "$ref": "#/definitions/io.k8s.api.discovery.v1.EndpointConditions", + "description": "conditions contains information about the current status of the endpoint." + }, + "deprecatedTopology": { + "additionalProperties": { + "type": "string" + }, + "description": "deprecatedTopology contains topology information part of the v1beta1 API. This field is deprecated, and will be removed when the v1beta1 API is removed (no sooner than kubernetes v1.24). While this field can hold values, it is not writable through the v1 API, and any attempts to write to it will be silently ignored. Topology information can be found in the zone and nodeName fields instead.", + "type": "object" + }, + "hints": { + "$ref": "#/definitions/io.k8s.api.discovery.v1.EndpointHints", + "description": "hints contains information associated with how an endpoint should be consumed." + }, + "hostname": { + "description": "hostname of this endpoint. This field may be used by consumers of endpoints to distinguish endpoints from each other (e.g. in DNS names). Multiple endpoints which use the same hostname should be considered fungible (e.g. multiple A values in DNS). Must be lowercase and pass DNS Label (RFC 1123) validation.", + "type": "string" + }, + "nodeName": { + "description": "nodeName represents the name of the Node hosting this endpoint. This can be used to determine endpoints local to a Node.", + "type": "string" + }, + "targetRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "targetRef is a reference to a Kubernetes object that represents this endpoint." + }, + "zone": { + "description": "zone is the name of the Zone this endpoint exists in.", + "type": "string" + } + }, + "required": [ + "addresses" + ], + "type": "object" + }, + "io.k8s.api.discovery.v1.EndpointConditions": { + "description": "EndpointConditions represents the current condition of an endpoint.", + "properties": { + "ready": { + "description": "ready indicates that this endpoint is prepared to receive traffic, according to whatever system is managing the endpoint. A nil value indicates an unknown state. In most cases consumers should interpret this unknown state as ready. For compatibility reasons, ready should never be \"true\" for terminating endpoints, except when the normal readiness behavior is being explicitly overridden, for example when the associated Service has set the publishNotReadyAddresses flag.", + "type": "boolean" + }, + "serving": { + "description": "serving is identical to ready except that it is set regardless of the terminating state of endpoints. This condition should be set to true for a ready endpoint that is terminating. If nil, consumers should defer to the ready condition.", + "type": "boolean" + }, + "terminating": { + "description": "terminating indicates that this endpoint is terminating. A nil value indicates an unknown state. Consumers should interpret this unknown state to mean that the endpoint is not terminating.", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.discovery.v1.EndpointHints": { + "description": "EndpointHints provides hints describing how an endpoint should be consumed.", + "properties": { + "forZones": { + "description": "forZones indicates the zone(s) this endpoint should be consumed by to enable topology aware routing.", + "items": { + "$ref": "#/definitions/io.k8s.api.discovery.v1.ForZone" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.discovery.v1.EndpointPort": { + "description": "EndpointPort represents a Port used by an EndpointSlice", + "properties": { + "appProtocol": { + "description": "The application protocol for this port. This is used as a hint for implementations to offer richer behavior for protocols that they understand. This field follows standard Kubernetes label syntax. Valid values are either:\n\n* Un-prefixed protocol names - reserved for IANA standard service names (as per RFC-6335 and https://www.iana.org/assignments/service-names).\n\n* Kubernetes-defined prefixed names:\n * 'kubernetes.io/h2c' - HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540\n * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455\n * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455\n\n* Other protocols should use implementation-defined prefixed names such as mycompany.com/my-custom-protocol.", + "type": "string" + }, + "name": { + "description": "name represents the name of this port. All ports in an EndpointSlice must have a unique name. If the EndpointSlice is dervied from a Kubernetes service, this corresponds to the Service.ports[].name. Name must either be an empty string or pass DNS_LABEL validation: * must be no more than 63 characters long. * must consist of lower case alphanumeric characters or '-'. * must start and end with an alphanumeric character. Default is empty string.", + "type": "string" + }, + "port": { + "description": "port represents the port number of the endpoint. If this is not specified, ports are not restricted and must be interpreted in the context of the specific consumer.", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "protocol represents the IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.discovery.v1.EndpointSlice": { + "description": "EndpointSlice represents a subset of the endpoints that implement a service. For a given service there may be multiple EndpointSlice objects, selected by labels, which must be joined to produce the full set of endpoints.", + "properties": { + "addressType": { + "description": "addressType specifies the type of address carried by this EndpointSlice. All addresses in this slice must be the same type. This field is immutable after creation. The following address types are currently supported: * IPv4: Represents an IPv4 Address. * IPv6: Represents an IPv6 Address. * FQDN: Represents a Fully Qualified Domain Name.", + "type": "string" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "endpoints": { + "description": "endpoints is a list of unique endpoints in this slice. Each slice may include a maximum of 1000 endpoints.", + "items": { + "$ref": "#/definitions/io.k8s.api.discovery.v1.Endpoint" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EndpointSlice" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "ports": { + "description": "ports specifies the list of network ports exposed by each endpoint in this slice. Each port must have a unique name. When ports is empty, it indicates that there are no defined ports. When a port is defined with a nil port value, it indicates \"all ports\". Each slice may include a maximum of 100 ports.", + "items": { + "$ref": "#/definitions/io.k8s.api.discovery.v1.EndpointPort" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "addressType", + "endpoints" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "discovery.k8s.io", + "kind": "EndpointSlice", + "version": "v1" + } + ] + }, + "io.k8s.api.discovery.v1.EndpointSliceList": { + "description": "EndpointSliceList represents a list of endpoint slices", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of endpoint slices", + "items": { + "$ref": "#/definitions/io.k8s.api.discovery.v1.EndpointSlice" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EndpointSliceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "discovery.k8s.io", + "kind": "EndpointSliceList", + "version": "v1" + } + ] + }, + "io.k8s.api.discovery.v1.ForZone": { + "description": "ForZone provides information about which zones should consume this endpoint.", + "properties": { + "name": { + "description": "name represents the name of the zone.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.events.v1.Event": { + "description": "Event is a report of an event somewhere in the cluster. It generally denotes some state change in the system. Events have a limited retention time and triggers and messages may evolve with time. Event consumers should not rely on the timing of an event with a given Reason reflecting a consistent underlying trigger, or the continued existence of events with that Reason. Events should be treated as informative, best-effort, supplemental data.", + "properties": { + "action": { + "description": "action is what action was taken/failed regarding to the regarding object. It is machine-readable. This field cannot be empty for new Events and it can have at most 128 characters.", + "type": "string" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "deprecatedCount": { + "description": "deprecatedCount is the deprecated field assuring backward compatibility with core.v1 Event type.", + "format": "int32", + "type": "integer" + }, + "deprecatedFirstTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "deprecatedFirstTimestamp is the deprecated field assuring backward compatibility with core.v1 Event type." + }, + "deprecatedLastTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "deprecatedLastTimestamp is the deprecated field assuring backward compatibility with core.v1 Event type." + }, + "deprecatedSource": { + "$ref": "#/definitions/io.k8s.api.core.v1.EventSource", + "description": "deprecatedSource is the deprecated field assuring backward compatibility with core.v1 Event type." + }, + "eventTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "eventTime is the time when this Event was first observed. It is required." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Event" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "note": { + "description": "note is a human-readable description of the status of this operation. Maximal length of the note is 1kB, but libraries should be prepared to handle values up to 64kB.", + "type": "string" + }, + "reason": { + "description": "reason is why the action was taken. It is human-readable. This field cannot be empty for new Events and it can have at most 128 characters.", + "type": "string" + }, + "regarding": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "regarding contains the object this Event is about. In most cases it's an Object reporting controller implements, e.g. ReplicaSetController implements ReplicaSets and this event is emitted because it acts on some changes in a ReplicaSet object." + }, + "related": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference", + "description": "related is the optional secondary object for more complex actions. E.g. when regarding object triggers a creation or deletion of related object." + }, + "reportingController": { + "description": "reportingController is the name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`. This field cannot be empty for new Events.", + "type": "string" + }, + "reportingInstance": { + "description": "reportingInstance is the ID of the controller instance, e.g. `kubelet-xyzf`. This field cannot be empty for new Events and it can have at most 128 characters.", + "type": "string" + }, + "series": { + "$ref": "#/definitions/io.k8s.api.events.v1.EventSeries", + "description": "series is data about the Event series this event represents or nil if it's a singleton Event." + }, + "type": { + "description": "type is the type of this event (Normal, Warning), new types could be added in the future. It is machine-readable. This field cannot be empty for new Events.", + "type": "string" + } + }, + "required": [ + "eventTime" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "events.k8s.io", + "kind": "Event", + "version": "v1" + } + ] + }, + "io.k8s.api.events.v1.EventList": { + "description": "EventList is a list of Event objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.events.v1.Event" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "EventList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "events.k8s.io", + "kind": "EventList", + "version": "v1" + } + ] + }, + "io.k8s.api.events.v1.EventSeries": { + "description": "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time. How often to update the EventSeries is up to the event reporters. The default event reporter in \"k8s.io/client-go/tools/events/event_broadcaster.go\" shows how this struct is updated on heartbeats and can guide customized reporter implementations.", + "properties": { + "count": { + "description": "count is the number of occurrences in this series up to the last heartbeat time.", + "format": "int32", + "type": "integer" + }, + "lastObservedTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime", + "description": "lastObservedTime is the time when last Event from the series was seen before last heartbeat." + } + }, + "required": [ + "count", + "lastObservedTime" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.ExemptPriorityLevelConfiguration": { + "description": "ExemptPriorityLevelConfiguration describes the configurable aspects of the handling of exempt requests. In the mandatory exempt configuration object the values in the fields here can be modified by authorized users, unlike the rest of the `spec`.", + "properties": { + "lendablePercent": { + "description": "`lendablePercent` prescribes the fraction of the level's NominalCL that can be borrowed by other priority levels. This value of this field must be between 0 and 100, inclusive, and it defaults to 0. The number of seats that other levels can borrow from this level, known as this level's LendableConcurrencyLimit (LendableCL), is defined as follows.\n\nLendableCL(i) = round( NominalCL(i) * lendablePercent(i)/100.0 )", + "format": "int32", + "type": "integer" + }, + "nominalConcurrencyShares": { + "description": "`nominalConcurrencyShares` (NCS) contributes to the computation of the NominalConcurrencyLimit (NominalCL) of this level. This is the number of execution seats nominally reserved for this priority level. This DOES NOT limit the dispatching from this priority level but affects the other priority levels through the borrowing mechanism. The server's concurrency limit (ServerCL) is divided among all the priority levels in proportion to their NCS values:\n\nNominalCL(i) = ceil( ServerCL * NCS(i) / sum_ncs ) sum_ncs = sum[priority level k] NCS(k)\n\nBigger numbers mean a larger nominal concurrency limit, at the expense of every other priority level. This field has a default value of zero.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.FlowDistinguisherMethod": { + "description": "FlowDistinguisherMethod specifies the method of a flow distinguisher.", + "properties": { + "type": { + "description": "`type` is the type of flow distinguisher method The supported types are \"ByUser\" and \"ByNamespace\". Required.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.FlowSchema": { + "description": "FlowSchema defines the schema of a group of flows. Note that a flow is made up of a set of inbound API requests with similar attributes and is identified by a pair of strings: the name of the FlowSchema and a \"flow distinguisher\".", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "FlowSchema" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "`metadata` is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.FlowSchemaSpec", + "description": "`spec` is the specification of the desired behavior of a FlowSchema. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.FlowSchemaStatus", + "description": "`status` is the current status of a FlowSchema. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "FlowSchema", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.FlowSchemaCondition": { + "description": "FlowSchemaCondition describes conditions for a FlowSchema.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "`lastTransitionTime` is the last time the condition transitioned from one status to another." + }, + "message": { + "description": "`message` is a human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "`reason` is a unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "`status` is the status of the condition. Can be True, False, Unknown. Required.", + "type": "string" + }, + "type": { + "description": "`type` is the type of the condition. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.FlowSchemaList": { + "description": "FlowSchemaList is a list of FlowSchema objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "`items` is a list of FlowSchemas.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.FlowSchema" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "FlowSchemaList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "`metadata` is the standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "FlowSchemaList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.FlowSchemaSpec": { + "description": "FlowSchemaSpec describes how the FlowSchema's specification looks like.", + "properties": { + "distinguisherMethod": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.FlowDistinguisherMethod", + "description": "`distinguisherMethod` defines how to compute the flow distinguisher for requests that match this schema. `nil` specifies that the distinguisher is disabled and thus will always be the empty string." + }, + "matchingPrecedence": { + "description": "`matchingPrecedence` is used to choose among the FlowSchemas that match a given request. The chosen FlowSchema is among those with the numerically lowest (which we take to be logically highest) MatchingPrecedence. Each MatchingPrecedence value must be ranged in [1,10000]. Note that if the precedence is not specified, it will be set to 1000 as default.", + "format": "int32", + "type": "integer" + }, + "priorityLevelConfiguration": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationReference", + "description": "`priorityLevelConfiguration` should reference a PriorityLevelConfiguration in the cluster. If the reference cannot be resolved, the FlowSchema will be ignored and marked as invalid in its status. Required." + }, + "rules": { + "description": "`rules` describes which requests will match this flow schema. This FlowSchema matches a request if and only if at least one member of rules matches the request. if it is an empty slice, there will be no requests matching the FlowSchema.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.PolicyRulesWithSubjects" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "priorityLevelConfiguration" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.FlowSchemaStatus": { + "description": "FlowSchemaStatus represents the current state of a FlowSchema.", + "properties": { + "conditions": { + "description": "`conditions` is a list of the current states of FlowSchema.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.FlowSchemaCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.GroupSubject": { + "description": "GroupSubject holds detailed information for group-kind subject.", + "properties": { + "name": { + "description": "name is the user group that matches, or \"*\" to match all user groups. See https://github.com/kubernetes/apiserver/blob/master/pkg/authentication/user/user.go for some well-known group names. Required.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.LimitResponse": { + "description": "LimitResponse defines how to handle requests that can not be executed right now.", + "properties": { + "queuing": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.QueuingConfiguration", + "description": "`queuing` holds the configuration parameters for queuing. This field may be non-empty only if `type` is `\"Queue\"`." + }, + "type": { + "description": "`type` is \"Queue\" or \"Reject\". \"Queue\" means that requests that can not be executed upon arrival are held in a queue until they can be executed or a queuing limit is reached. \"Reject\" means that requests that can not be executed upon arrival are rejected. Required.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "queuing": "Queuing" + } + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.LimitedPriorityLevelConfiguration": { + "description": "LimitedPriorityLevelConfiguration specifies how to handle requests that are subject to limits. It addresses two issues:\n - How are requests for this priority level limited?\n - What should be done with requests that exceed the limit?", + "properties": { + "assuredConcurrencyShares": { + "description": "`assuredConcurrencyShares` (ACS) configures the execution limit, which is a limit on the number of requests of this priority level that may be exeucting at a given time. ACS must be a positive number. The server's concurrency limit (SCL) is divided among the concurrency-controlled priority levels in proportion to their assured concurrency shares. This produces the assured concurrency value (ACV) --- the number of requests that may be executing at a time --- for each such priority level:\n\n ACV(l) = ceil( SCL * ACS(l) / ( sum[priority levels k] ACS(k) ) )\n\nbigger numbers of ACS mean more reserved concurrent requests (at the expense of every other PL). This field has a default value of 30.", + "format": "int32", + "type": "integer" + }, + "borrowingLimitPercent": { + "description": "`borrowingLimitPercent`, if present, configures a limit on how many seats this priority level can borrow from other priority levels. The limit is known as this level's BorrowingConcurrencyLimit (BorrowingCL) and is a limit on the total number of seats that this level may borrow at any one time. This field holds the ratio of that limit to the level's nominal concurrency limit. When this field is non-nil, it must hold a non-negative integer and the limit is calculated as follows.\n\nBorrowingCL(i) = round( NominalCL(i) * borrowingLimitPercent(i)/100.0 )\n\nThe value of this field can be more than 100, implying that this priority level can borrow a number of seats that is greater than its own nominal concurrency limit (NominalCL). When this field is left `nil`, the limit is effectively infinite.", + "format": "int32", + "type": "integer" + }, + "lendablePercent": { + "description": "`lendablePercent` prescribes the fraction of the level's NominalCL that can be borrowed by other priority levels. The value of this field must be between 0 and 100, inclusive, and it defaults to 0. The number of seats that other levels can borrow from this level, known as this level's LendableConcurrencyLimit (LendableCL), is defined as follows.\n\nLendableCL(i) = round( NominalCL(i) * lendablePercent(i)/100.0 )", + "format": "int32", + "type": "integer" + }, + "limitResponse": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.LimitResponse", + "description": "`limitResponse` indicates what to do with requests that can not be executed right now" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.NonResourcePolicyRule": { + "description": "NonResourcePolicyRule is a predicate that matches non-resource requests according to their verb and the target non-resource URL. A NonResourcePolicyRule matches a request if and only if both (a) at least one member of verbs matches the request and (b) at least one member of nonResourceURLs matches the request.", + "properties": { + "nonResourceURLs": { + "description": "`nonResourceURLs` is a set of url prefixes that a user should have access to and may not be empty. For example:\n - \"/healthz\" is legal\n - \"/hea*\" is illegal\n - \"/hea\" is legal but matches nothing\n - \"/hea/*\" also matches nothing\n - \"/healthz/*\" matches all per-component health checks.\n\"*\" matches all non-resource urls. if it is present, it must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "verbs": { + "description": "`verbs` is a list of matching verbs and may not be empty. \"*\" matches all verbs. If it is present, it must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "required": [ + "verbs", + "nonResourceURLs" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.PolicyRulesWithSubjects": { + "description": "PolicyRulesWithSubjects prescribes a test that applies to a request to an apiserver. The test considers the subject making the request, the verb being requested, and the resource to be acted upon. This PolicyRulesWithSubjects matches a request if and only if both (a) at least one member of subjects matches the request and (b) at least one member of resourceRules or nonResourceRules matches the request.", + "properties": { + "nonResourceRules": { + "description": "`nonResourceRules` is a list of NonResourcePolicyRules that identify matching requests according to their verb and the target non-resource URL.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.NonResourcePolicyRule" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resourceRules": { + "description": "`resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the target resource. At least one of `resourceRules` and `nonResourceRules` has to be non-empty.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.ResourcePolicyRule" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "subjects": { + "description": "subjects is the list of normal user, serviceaccount, or group that this rule cares about. There must be at least one member in this slice. A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request. Required.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.Subject" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "subjects" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfiguration": { + "description": "PriorityLevelConfiguration represents the configuration of a priority level.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityLevelConfiguration" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "`metadata` is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationSpec", + "description": "`spec` is the specification of the desired behavior of a \"request-priority\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationStatus", + "description": "`status` is the current status of a \"request-priority\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "PriorityLevelConfiguration", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationCondition": { + "description": "PriorityLevelConfigurationCondition defines the condition of priority level.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "`lastTransitionTime` is the last time the condition transitioned from one status to another." + }, + "message": { + "description": "`message` is a human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "`reason` is a unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "`status` is the status of the condition. Can be True, False, Unknown. Required.", + "type": "string" + }, + "type": { + "description": "`type` is the type of the condition. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationList": { + "description": "PriorityLevelConfigurationList is a list of PriorityLevelConfiguration objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "`items` is a list of request-priorities.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityLevelConfigurationList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "`metadata` is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "PriorityLevelConfigurationList", + "version": "v1beta2" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationReference": { + "description": "PriorityLevelConfigurationReference contains information that points to the \"request-priority\" being used.", + "properties": { + "name": { + "description": "`name` is the name of the priority level configuration being referenced Required.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationSpec": { + "description": "PriorityLevelConfigurationSpec specifies the configuration of a priority level.", + "properties": { + "exempt": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.ExemptPriorityLevelConfiguration", + "description": "`exempt` specifies how requests are handled for an exempt priority level. This field MUST be empty if `type` is `\"Limited\"`. This field MAY be non-empty if `type` is `\"Exempt\"`. If empty and `type` is `\"Exempt\"` then the default values for `ExemptPriorityLevelConfiguration` apply." + }, + "limited": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.LimitedPriorityLevelConfiguration", + "description": "`limited` specifies how requests are handled for a Limited priority level. This field must be non-empty if and only if `type` is `\"Limited\"`." + }, + "type": { + "description": "`type` indicates whether this priority level is subject to limitation on request execution. A value of `\"Exempt\"` means that requests of this priority level are not subject to a limit (and thus are never queued) and do not detract from the capacity made available to other priority levels. A value of `\"Limited\"` means that (a) requests of this priority level _are_ subject to limits and (b) some of the server's limited capacity is made available exclusively to this priority level. Required.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "exempt": "Exempt", + "limited": "Limited" + } + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationStatus": { + "description": "PriorityLevelConfigurationStatus represents the current state of a \"request-priority\".", + "properties": { + "conditions": { + "description": "`conditions` is the current state of \"request-priority\".", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.PriorityLevelConfigurationCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.QueuingConfiguration": { + "description": "QueuingConfiguration holds the configuration parameters for queuing", + "properties": { + "handSize": { + "description": "`handSize` is a small positive number that configures the shuffle sharding of requests into queues. When enqueuing a request at this priority level the request's flow identifier (a string pair) is hashed and the hash value is used to shuffle the list of queues and deal a hand of the size specified here. The request is put into one of the shortest queues in that hand. `handSize` must be no larger than `queues`, and should be significantly smaller (so that a few heavy flows do not saturate most of the queues). See the user-facing documentation for more extensive guidance on setting this field. This field has a default value of 8.", + "format": "int32", + "type": "integer" + }, + "queueLengthLimit": { + "description": "`queueLengthLimit` is the maximum number of requests allowed to be waiting in a given queue of this priority level at a time; excess requests are rejected. This value must be positive. If not specified, it will be defaulted to 50.", + "format": "int32", + "type": "integer" + }, + "queues": { + "description": "`queues` is the number of queues for this priority level. The queues exist independently at each apiserver. The value must be positive. Setting it to 1 effectively precludes shufflesharding and thus makes the distinguisher method of associated flow schemas irrelevant. This field has a default value of 64.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.ResourcePolicyRule": { + "description": "ResourcePolicyRule is a predicate that matches some resource requests, testing the request's verb and the target resource. A ResourcePolicyRule matches a resource request if and only if: (a) at least one member of verbs matches the request, (b) at least one member of apiGroups matches the request, (c) at least one member of resources matches the request, and (d) either (d1) the request does not specify a namespace (i.e., `Namespace==\"\"`) and clusterScope is true or (d2) the request specifies a namespace and least one member of namespaces matches the request's namespace.", + "properties": { + "apiGroups": { + "description": "`apiGroups` is a list of matching API groups and may not be empty. \"*\" matches all API groups and, if present, must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "clusterScope": { + "description": "`clusterScope` indicates whether to match requests that do not specify a namespace (which happens either because the resource is not namespaced or the request targets all namespaces). If this field is omitted or false then the `namespaces` field must contain a non-empty list.", + "type": "boolean" + }, + "namespaces": { + "description": "`namespaces` is a list of target namespaces that restricts matches. A request that specifies a target namespace matches only if either (a) this list contains that target namespace or (b) this list contains \"*\". Note that \"*\" matches any specified namespace but does not match a request that _does not specify_ a namespace (see the `clusterScope` field for that). This list may be empty, but only if `clusterScope` is true.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "resources": { + "description": "`resources` is a list of matching resources (i.e., lowercase and plural) with, if desired, subresource. For example, [ \"services\", \"nodes/status\" ]. This list may not be empty. \"*\" matches all resources and, if present, must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "verbs": { + "description": "`verbs` is a list of matching verbs and may not be empty. \"*\" matches all verbs and, if present, must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "required": [ + "verbs", + "apiGroups", + "resources" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.ServiceAccountSubject": { + "description": "ServiceAccountSubject holds detailed information for service-account-kind subject.", + "properties": { + "name": { + "description": "`name` is the name of matching ServiceAccount objects, or \"*\" to match regardless of name. Required.", + "type": "string" + }, + "namespace": { + "description": "`namespace` is the namespace of matching ServiceAccount objects. Required.", + "type": "string" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta2.Subject": { + "description": "Subject matches the originator of a request, as identified by the request authentication system. There are three ways of matching an originator; by user, group, or service account.", + "properties": { + "group": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.GroupSubject", + "description": "`group` matches based on user group name." + }, + "kind": { + "description": "`kind` indicates which one of the other fields is non-empty. Required", + "type": "string" + }, + "serviceAccount": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.ServiceAccountSubject", + "description": "`serviceAccount` matches ServiceAccounts." + }, + "user": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta2.UserSubject", + "description": "`user` matches based on username." + } + }, + "required": [ + "kind" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "kind", + "fields-to-discriminateBy": { + "group": "Group", + "serviceAccount": "ServiceAccount", + "user": "User" + } + } + ] + }, + "io.k8s.api.flowcontrol.v1beta2.UserSubject": { + "description": "UserSubject holds detailed information for user-kind subject.", + "properties": { + "name": { + "description": "`name` is the username that matches, or \"*\" to match all usernames. Required.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.ExemptPriorityLevelConfiguration": { + "description": "ExemptPriorityLevelConfiguration describes the configurable aspects of the handling of exempt requests. In the mandatory exempt configuration object the values in the fields here can be modified by authorized users, unlike the rest of the `spec`.", + "properties": { + "lendablePercent": { + "description": "`lendablePercent` prescribes the fraction of the level's NominalCL that can be borrowed by other priority levels. This value of this field must be between 0 and 100, inclusive, and it defaults to 0. The number of seats that other levels can borrow from this level, known as this level's LendableConcurrencyLimit (LendableCL), is defined as follows.\n\nLendableCL(i) = round( NominalCL(i) * lendablePercent(i)/100.0 )", + "format": "int32", + "type": "integer" + }, + "nominalConcurrencyShares": { + "description": "`nominalConcurrencyShares` (NCS) contributes to the computation of the NominalConcurrencyLimit (NominalCL) of this level. This is the number of execution seats nominally reserved for this priority level. This DOES NOT limit the dispatching from this priority level but affects the other priority levels through the borrowing mechanism. The server's concurrency limit (ServerCL) is divided among all the priority levels in proportion to their NCS values:\n\nNominalCL(i) = ceil( ServerCL * NCS(i) / sum_ncs ) sum_ncs = sum[priority level k] NCS(k)\n\nBigger numbers mean a larger nominal concurrency limit, at the expense of every other priority level. This field has a default value of zero.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.FlowDistinguisherMethod": { + "description": "FlowDistinguisherMethod specifies the method of a flow distinguisher.", + "properties": { + "type": { + "description": "`type` is the type of flow distinguisher method The supported types are \"ByUser\" and \"ByNamespace\". Required.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.FlowSchema": { + "description": "FlowSchema defines the schema of a group of flows. Note that a flow is made up of a set of inbound API requests with similar attributes and is identified by a pair of strings: the name of the FlowSchema and a \"flow distinguisher\".", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "FlowSchema" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "`metadata` is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.FlowSchemaSpec", + "description": "`spec` is the specification of the desired behavior of a FlowSchema. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.FlowSchemaStatus", + "description": "`status` is the current status of a FlowSchema. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "FlowSchema", + "version": "v1beta3" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.FlowSchemaCondition": { + "description": "FlowSchemaCondition describes conditions for a FlowSchema.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "`lastTransitionTime` is the last time the condition transitioned from one status to another." + }, + "message": { + "description": "`message` is a human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "`reason` is a unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "`status` is the status of the condition. Can be True, False, Unknown. Required.", + "type": "string" + }, + "type": { + "description": "`type` is the type of the condition. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.FlowSchemaList": { + "description": "FlowSchemaList is a list of FlowSchema objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "`items` is a list of FlowSchemas.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.FlowSchema" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "FlowSchemaList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "`metadata` is the standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "FlowSchemaList", + "version": "v1beta3" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.FlowSchemaSpec": { + "description": "FlowSchemaSpec describes how the FlowSchema's specification looks like.", + "properties": { + "distinguisherMethod": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.FlowDistinguisherMethod", + "description": "`distinguisherMethod` defines how to compute the flow distinguisher for requests that match this schema. `nil` specifies that the distinguisher is disabled and thus will always be the empty string." + }, + "matchingPrecedence": { + "description": "`matchingPrecedence` is used to choose among the FlowSchemas that match a given request. The chosen FlowSchema is among those with the numerically lowest (which we take to be logically highest) MatchingPrecedence. Each MatchingPrecedence value must be ranged in [1,10000]. Note that if the precedence is not specified, it will be set to 1000 as default.", + "format": "int32", + "type": "integer" + }, + "priorityLevelConfiguration": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationReference", + "description": "`priorityLevelConfiguration` should reference a PriorityLevelConfiguration in the cluster. If the reference cannot be resolved, the FlowSchema will be ignored and marked as invalid in its status. Required." + }, + "rules": { + "description": "`rules` describes which requests will match this flow schema. This FlowSchema matches a request if and only if at least one member of rules matches the request. if it is an empty slice, there will be no requests matching the FlowSchema.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.PolicyRulesWithSubjects" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "priorityLevelConfiguration" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.FlowSchemaStatus": { + "description": "FlowSchemaStatus represents the current state of a FlowSchema.", + "properties": { + "conditions": { + "description": "`conditions` is a list of the current states of FlowSchema.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.FlowSchemaCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.GroupSubject": { + "description": "GroupSubject holds detailed information for group-kind subject.", + "properties": { + "name": { + "description": "name is the user group that matches, or \"*\" to match all user groups. See https://github.com/kubernetes/apiserver/blob/master/pkg/authentication/user/user.go for some well-known group names. Required.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.LimitResponse": { + "description": "LimitResponse defines how to handle requests that can not be executed right now.", + "properties": { + "queuing": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.QueuingConfiguration", + "description": "`queuing` holds the configuration parameters for queuing. This field may be non-empty only if `type` is `\"Queue\"`." + }, + "type": { + "description": "`type` is \"Queue\" or \"Reject\". \"Queue\" means that requests that can not be executed upon arrival are held in a queue until they can be executed or a queuing limit is reached. \"Reject\" means that requests that can not be executed upon arrival are rejected. Required.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "queuing": "Queuing" + } + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.LimitedPriorityLevelConfiguration": { + "description": "LimitedPriorityLevelConfiguration specifies how to handle requests that are subject to limits. It addresses two issues:\n - How are requests for this priority level limited?\n - What should be done with requests that exceed the limit?", + "properties": { + "borrowingLimitPercent": { + "description": "`borrowingLimitPercent`, if present, configures a limit on how many seats this priority level can borrow from other priority levels. The limit is known as this level's BorrowingConcurrencyLimit (BorrowingCL) and is a limit on the total number of seats that this level may borrow at any one time. This field holds the ratio of that limit to the level's nominal concurrency limit. When this field is non-nil, it must hold a non-negative integer and the limit is calculated as follows.\n\nBorrowingCL(i) = round( NominalCL(i) * borrowingLimitPercent(i)/100.0 )\n\nThe value of this field can be more than 100, implying that this priority level can borrow a number of seats that is greater than its own nominal concurrency limit (NominalCL). When this field is left `nil`, the limit is effectively infinite.", + "format": "int32", + "type": "integer" + }, + "lendablePercent": { + "description": "`lendablePercent` prescribes the fraction of the level's NominalCL that can be borrowed by other priority levels. The value of this field must be between 0 and 100, inclusive, and it defaults to 0. The number of seats that other levels can borrow from this level, known as this level's LendableConcurrencyLimit (LendableCL), is defined as follows.\n\nLendableCL(i) = round( NominalCL(i) * lendablePercent(i)/100.0 )", + "format": "int32", + "type": "integer" + }, + "limitResponse": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.LimitResponse", + "description": "`limitResponse` indicates what to do with requests that can not be executed right now" + }, + "nominalConcurrencyShares": { + "description": "`nominalConcurrencyShares` (NCS) contributes to the computation of the NominalConcurrencyLimit (NominalCL) of this level. This is the number of execution seats available at this priority level. This is used both for requests dispatched from this priority level as well as requests dispatched from other priority levels borrowing seats from this level. The server's concurrency limit (ServerCL) is divided among the Limited priority levels in proportion to their NCS values:\n\nNominalCL(i) = ceil( ServerCL * NCS(i) / sum_ncs ) sum_ncs = sum[priority level k] NCS(k)\n\nBigger numbers mean a larger nominal concurrency limit, at the expense of every other priority level. This field has a default value of 30.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.NonResourcePolicyRule": { + "description": "NonResourcePolicyRule is a predicate that matches non-resource requests according to their verb and the target non-resource URL. A NonResourcePolicyRule matches a request if and only if both (a) at least one member of verbs matches the request and (b) at least one member of nonResourceURLs matches the request.", + "properties": { + "nonResourceURLs": { + "description": "`nonResourceURLs` is a set of url prefixes that a user should have access to and may not be empty. For example:\n - \"/healthz\" is legal\n - \"/hea*\" is illegal\n - \"/hea\" is legal but matches nothing\n - \"/hea/*\" also matches nothing\n - \"/healthz/*\" matches all per-component health checks.\n\"*\" matches all non-resource urls. if it is present, it must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "verbs": { + "description": "`verbs` is a list of matching verbs and may not be empty. \"*\" matches all verbs. If it is present, it must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "required": [ + "verbs", + "nonResourceURLs" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.PolicyRulesWithSubjects": { + "description": "PolicyRulesWithSubjects prescribes a test that applies to a request to an apiserver. The test considers the subject making the request, the verb being requested, and the resource to be acted upon. This PolicyRulesWithSubjects matches a request if and only if both (a) at least one member of subjects matches the request and (b) at least one member of resourceRules or nonResourceRules matches the request.", + "properties": { + "nonResourceRules": { + "description": "`nonResourceRules` is a list of NonResourcePolicyRules that identify matching requests according to their verb and the target non-resource URL.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.NonResourcePolicyRule" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "resourceRules": { + "description": "`resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the target resource. At least one of `resourceRules` and `nonResourceRules` has to be non-empty.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.ResourcePolicyRule" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "subjects": { + "description": "subjects is the list of normal user, serviceaccount, or group that this rule cares about. There must be at least one member in this slice. A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request. Required.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.Subject" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "subjects" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfiguration": { + "description": "PriorityLevelConfiguration represents the configuration of a priority level.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityLevelConfiguration" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "`metadata` is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationSpec", + "description": "`spec` is the specification of the desired behavior of a \"request-priority\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationStatus", + "description": "`status` is the current status of a \"request-priority\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "PriorityLevelConfiguration", + "version": "v1beta3" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationCondition": { + "description": "PriorityLevelConfigurationCondition defines the condition of priority level.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "`lastTransitionTime` is the last time the condition transitioned from one status to another." + }, + "message": { + "description": "`message` is a human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "`reason` is a unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "`status` is the status of the condition. Can be True, False, Unknown. Required.", + "type": "string" + }, + "type": { + "description": "`type` is the type of the condition. Required.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationList": { + "description": "PriorityLevelConfigurationList is a list of PriorityLevelConfiguration objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "`items` is a list of request-priorities.", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityLevelConfigurationList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "`metadata` is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "PriorityLevelConfigurationList", + "version": "v1beta3" + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationReference": { + "description": "PriorityLevelConfigurationReference contains information that points to the \"request-priority\" being used.", + "properties": { + "name": { + "description": "`name` is the name of the priority level configuration being referenced Required.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationSpec": { + "description": "PriorityLevelConfigurationSpec specifies the configuration of a priority level.", + "properties": { + "exempt": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.ExemptPriorityLevelConfiguration", + "description": "`exempt` specifies how requests are handled for an exempt priority level. This field MUST be empty if `type` is `\"Limited\"`. This field MAY be non-empty if `type` is `\"Exempt\"`. If empty and `type` is `\"Exempt\"` then the default values for `ExemptPriorityLevelConfiguration` apply." + }, + "limited": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.LimitedPriorityLevelConfiguration", + "description": "`limited` specifies how requests are handled for a Limited priority level. This field must be non-empty if and only if `type` is `\"Limited\"`." + }, + "type": { + "description": "`type` indicates whether this priority level is subject to limitation on request execution. A value of `\"Exempt\"` means that requests of this priority level are not subject to a limit (and thus are never queued) and do not detract from the capacity made available to other priority levels. A value of `\"Limited\"` means that (a) requests of this priority level _are_ subject to limits and (b) some of the server's limited capacity is made available exclusively to this priority level. Required.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "exempt": "Exempt", + "limited": "Limited" + } + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationStatus": { + "description": "PriorityLevelConfigurationStatus represents the current state of a \"request-priority\".", + "properties": { + "conditions": { + "description": "`conditions` is the current state of \"request-priority\".", + "items": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.PriorityLevelConfigurationCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.QueuingConfiguration": { + "description": "QueuingConfiguration holds the configuration parameters for queuing", + "properties": { + "handSize": { + "description": "`handSize` is a small positive number that configures the shuffle sharding of requests into queues. When enqueuing a request at this priority level the request's flow identifier (a string pair) is hashed and the hash value is used to shuffle the list of queues and deal a hand of the size specified here. The request is put into one of the shortest queues in that hand. `handSize` must be no larger than `queues`, and should be significantly smaller (so that a few heavy flows do not saturate most of the queues). See the user-facing documentation for more extensive guidance on setting this field. This field has a default value of 8.", + "format": "int32", + "type": "integer" + }, + "queueLengthLimit": { + "description": "`queueLengthLimit` is the maximum number of requests allowed to be waiting in a given queue of this priority level at a time; excess requests are rejected. This value must be positive. If not specified, it will be defaulted to 50.", + "format": "int32", + "type": "integer" + }, + "queues": { + "description": "`queues` is the number of queues for this priority level. The queues exist independently at each apiserver. The value must be positive. Setting it to 1 effectively precludes shufflesharding and thus makes the distinguisher method of associated flow schemas irrelevant. This field has a default value of 64.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.ResourcePolicyRule": { + "description": "ResourcePolicyRule is a predicate that matches some resource requests, testing the request's verb and the target resource. A ResourcePolicyRule matches a resource request if and only if: (a) at least one member of verbs matches the request, (b) at least one member of apiGroups matches the request, (c) at least one member of resources matches the request, and (d) either (d1) the request does not specify a namespace (i.e., `Namespace==\"\"`) and clusterScope is true or (d2) the request specifies a namespace and least one member of namespaces matches the request's namespace.", + "properties": { + "apiGroups": { + "description": "`apiGroups` is a list of matching API groups and may not be empty. \"*\" matches all API groups and, if present, must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "clusterScope": { + "description": "`clusterScope` indicates whether to match requests that do not specify a namespace (which happens either because the resource is not namespaced or the request targets all namespaces). If this field is omitted or false then the `namespaces` field must contain a non-empty list.", + "type": "boolean" + }, + "namespaces": { + "description": "`namespaces` is a list of target namespaces that restricts matches. A request that specifies a target namespace matches only if either (a) this list contains that target namespace or (b) this list contains \"*\". Note that \"*\" matches any specified namespace but does not match a request that _does not specify_ a namespace (see the `clusterScope` field for that). This list may be empty, but only if `clusterScope` is true.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "resources": { + "description": "`resources` is a list of matching resources (i.e., lowercase and plural) with, if desired, subresource. For example, [ \"services\", \"nodes/status\" ]. This list may not be empty. \"*\" matches all resources and, if present, must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "verbs": { + "description": "`verbs` is a list of matching verbs and may not be empty. \"*\" matches all verbs and, if present, must be the only entry. Required.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "required": [ + "verbs", + "apiGroups", + "resources" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.ServiceAccountSubject": { + "description": "ServiceAccountSubject holds detailed information for service-account-kind subject.", + "properties": { + "name": { + "description": "`name` is the name of matching ServiceAccount objects, or \"*\" to match regardless of name. Required.", + "type": "string" + }, + "namespace": { + "description": "`namespace` is the namespace of matching ServiceAccount objects. Required.", + "type": "string" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.api.flowcontrol.v1beta3.Subject": { + "description": "Subject matches the originator of a request, as identified by the request authentication system. There are three ways of matching an originator; by user, group, or service account.", + "properties": { + "group": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.GroupSubject", + "description": "`group` matches based on user group name." + }, + "kind": { + "description": "`kind` indicates which one of the other fields is non-empty. Required", + "type": "string" + }, + "serviceAccount": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.ServiceAccountSubject", + "description": "`serviceAccount` matches ServiceAccounts." + }, + "user": { + "$ref": "#/definitions/io.k8s.api.flowcontrol.v1beta3.UserSubject", + "description": "`user` matches based on username." + } + }, + "required": [ + "kind" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "kind", + "fields-to-discriminateBy": { + "group": "Group", + "serviceAccount": "ServiceAccount", + "user": "User" + } + } + ] + }, + "io.k8s.api.flowcontrol.v1beta3.UserSubject": { + "description": "UserSubject holds detailed information for user-kind subject.", + "properties": { + "name": { + "description": "`name` is the username that matches, or \"*\" to match all usernames. Required.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.HTTPIngressPath": { + "description": "HTTPIngressPath associates a path with a backend. Incoming urls matching the path are forwarded to the backend.", + "properties": { + "backend": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressBackend", + "description": "backend defines the referenced service endpoint to which the traffic will be forwarded to." + }, + "path": { + "description": "path is matched against the path of an incoming request. Currently it can contain characters disallowed from the conventional \"path\" part of a URL as defined by RFC 3986. Paths must begin with a '/' and must be present when using PathType with value \"Exact\" or \"Prefix\".", + "type": "string" + }, + "pathType": { + "description": "pathType determines the interpretation of the path matching. PathType can be one of the following values: * Exact: Matches the URL path exactly. * Prefix: Matches based on a URL path prefix split by '/'. Matching is\n done on a path element by element basis. A path element refers is the\n list of labels in the path split by the '/' separator. A request is a\n match for path p if every p is an element-wise prefix of p of the\n request path. Note that if the last element of the path is a substring\n of the last element in request path, it is not a match (e.g. /foo/bar\n matches /foo/bar/baz, but does not match /foo/barbaz).\n* ImplementationSpecific: Interpretation of the Path matching is up to\n the IngressClass. Implementations can treat this as a separate PathType\n or treat it identically to Prefix or Exact path types.\nImplementations are required to support all path types.", + "type": "string" + } + }, + "required": [ + "pathType", + "backend" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.HTTPIngressRuleValue": { + "description": "HTTPIngressRuleValue is a list of http selectors pointing to backends. In the example: http:///? -> backend where where parts of the url correspond to RFC 3986, this resource will be used to match against everything after the last '/' and before the first '?' or '#'.", + "properties": { + "paths": { + "description": "paths is a collection of paths that map requests to backends.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.HTTPIngressPath" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "paths" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.IPBlock": { + "description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.0/24\",\"2001:db8::/64\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", + "properties": { + "cidr": { + "description": "cidr is a string representing the IPBlock Valid examples are \"192.168.1.0/24\" or \"2001:db8::/64\"", + "type": "string" + }, + "except": { + "description": "except is a slice of CIDRs that should not be included within an IPBlock Valid examples are \"192.168.1.0/24\" or \"2001:db8::/64\" Except values will be rejected if they are outside the cidr range", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "cidr" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.Ingress": { + "description": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Ingress" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressSpec", + "description": "spec is the desired state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + }, + "status": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressStatus", + "description": "status is the current state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "Ingress", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.IngressBackend": { + "description": "IngressBackend describes all endpoints for a given service and port.", + "properties": { + "resource": { + "$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference", + "description": "resource is an ObjectRef to another Kubernetes resource in the namespace of the Ingress object. If resource is specified, a service.Name and service.Port must not be specified. This is a mutually exclusive setting with \"Service\"." + }, + "service": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressServiceBackend", + "description": "service references a service as a backend. This is a mutually exclusive setting with \"Resource\"." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressClass": { + "description": "IngressClass represents the class of the Ingress, referenced by the Ingress Spec. The `ingressclass.kubernetes.io/is-default-class` annotation can be used to indicate that an IngressClass should be considered default. When a single IngressClass resource has this annotation set to true, new Ingress resources without a class specified will be assigned this default class.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IngressClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressClassSpec", + "description": "spec is the desired state of the IngressClass. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.IngressClassList": { + "description": "IngressClassList is a collection of IngressClasses.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of IngressClasses.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IngressClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IngressClassList", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.IngressClassParametersReference": { + "description": "IngressClassParametersReference identifies an API object. This can be used to specify a cluster or namespace-scoped resource.", + "properties": { + "apiGroup": { + "description": "apiGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", + "type": "string" + }, + "kind": { + "description": "kind is the type of resource being referenced.", + "type": "string" + }, + "name": { + "description": "name is the name of resource being referenced.", + "type": "string" + }, + "namespace": { + "description": "namespace is the namespace of the resource being referenced. This field is required when scope is set to \"Namespace\" and must be unset when scope is set to \"Cluster\".", + "type": "string" + }, + "scope": { + "description": "scope represents if this refers to a cluster or namespace scoped resource. This may be set to \"Cluster\" (default) or \"Namespace\".", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.IngressClassSpec": { + "description": "IngressClassSpec provides information about the class of an Ingress.", + "properties": { + "controller": { + "description": "controller refers to the name of the controller that should handle this class. This allows for different \"flavors\" that are controlled by the same controller. For example, you may have different parameters for the same implementing controller. This should be specified as a domain-prefixed path no more than 250 characters in length, e.g. \"acme.io/ingress-controller\". This field is immutable.", + "type": "string" + }, + "parameters": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressClassParametersReference", + "description": "parameters is a link to a custom resource containing additional configuration for the controller. This is optional if the controller does not require extra parameters." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressList": { + "description": "IngressList is a collection of Ingress.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of Ingress.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.Ingress" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IngressList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IngressList", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.IngressLoadBalancerIngress": { + "description": "IngressLoadBalancerIngress represents the status of a load-balancer ingress point.", + "properties": { + "hostname": { + "description": "hostname is set for load-balancer ingress points that are DNS based.", + "type": "string" + }, + "ip": { + "description": "ip is set for load-balancer ingress points that are IP based.", + "type": "string" + }, + "ports": { + "description": "ports provides information about the ports exposed by this LoadBalancer.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressPortStatus" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressLoadBalancerStatus": { + "description": "IngressLoadBalancerStatus represents the status of a load-balancer.", + "properties": { + "ingress": { + "description": "ingress is a list containing ingress points for the load-balancer.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressLoadBalancerIngress" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressPortStatus": { + "description": "IngressPortStatus represents the error condition of a service port", + "properties": { + "error": { + "description": "error is to record the problem with the service port The format of the error shall comply with the following rules: - built-in error values shall be specified in this file and those shall use\n CamelCase names\n- cloud provider specific error values must have names that comply with the\n format foo.example.com/CamelCase.", + "type": "string" + }, + "port": { + "description": "port is the port number of the ingress port.", + "format": "int32", + "type": "integer" + }, + "protocol": { + "description": "protocol is the protocol of the ingress port. The supported values are: \"TCP\", \"UDP\", \"SCTP\"", + "type": "string" + } + }, + "required": [ + "port", + "protocol" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.IngressRule": { + "description": "IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.", + "properties": { + "host": { + "description": "host is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in RFC 3986: 1. IPs are not allowed. Currently an IngressRuleValue can only apply to\n the IP in the Spec of the parent Ingress.\n2. The `:` delimiter is not respected because ports are not allowed.\n\t Currently the port of an Ingress is implicitly :80 for http and\n\t :443 for https.\nBoth these may change in the future. Incoming requests are matched against the host before the IngressRuleValue. If the host is unspecified, the Ingress routes all traffic based on the specified IngressRuleValue.\n\nhost can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.bar.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. \"*.foo.com\"). The wildcard character '*' must appear by itself as the first DNS label and matches only a single label. You cannot have a wildcard label by itself (e.g. Host == \"*\"). Requests will be matched against the Host field in the following way: 1. If host is precise, the request matches this rule if the http host header is equal to Host. 2. If host is a wildcard, then the request matches this rule if the http host header is to equal to the suffix (removing the first label) of the wildcard rule.", + "type": "string" + }, + "http": { + "$ref": "#/definitions/io.k8s.api.networking.v1.HTTPIngressRuleValue" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressServiceBackend": { + "description": "IngressServiceBackend references a Kubernetes Service as a Backend.", + "properties": { + "name": { + "description": "name is the referenced service. The service must exist in the same namespace as the Ingress object.", + "type": "string" + }, + "port": { + "$ref": "#/definitions/io.k8s.api.networking.v1.ServiceBackendPort", + "description": "port of the referenced service. A port name or port number is required for a IngressServiceBackend." + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.IngressSpec": { + "description": "IngressSpec describes the Ingress the user wishes to exist.", + "properties": { + "defaultBackend": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressBackend", + "description": "defaultBackend is the backend that should handle requests that don't match any rule. If Rules are not specified, DefaultBackend must be specified. If DefaultBackend is not set, the handling of requests that do not match any of the rules will be up to the Ingress controller." + }, + "ingressClassName": { + "description": "ingressClassName is the name of an IngressClass cluster resource. Ingress controller implementations use this field to know whether they should be serving this Ingress resource, by a transitive connection (controller -> IngressClass -> Ingress resource). Although the `kubernetes.io/ingress.class` annotation (simple constant name) was never formally defined, it was widely supported by Ingress controllers to create a direct binding between Ingress controller and Ingress resources. Newly created Ingress resources should prefer using the field. However, even though the annotation is officially deprecated, for backwards compatibility reasons, ingress controllers should still honor that annotation if present.", + "type": "string" + }, + "rules": { + "description": "rules is a list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressRule" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "tls": { + "description": "tls represents the TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressTLS" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressStatus": { + "description": "IngressStatus describe the current state of the Ingress.", + "properties": { + "loadBalancer": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IngressLoadBalancerStatus", + "description": "loadBalancer contains the current status of the load-balancer." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.IngressTLS": { + "description": "IngressTLS describes the transport layer security associated with an ingress.", + "properties": { + "hosts": { + "description": "hosts is a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "secretName": { + "description": "secretName is the name of the secret used to terminate TLS traffic on port 443. Field is left optional to allow TLS routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the \"Host\" header is used for routing.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicy": { + "description": "NetworkPolicy describes what network traffic is allowed for a set of Pods", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NetworkPolicy" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicySpec", + "description": "spec represents the specification of the desired behavior for this NetworkPolicy." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "NetworkPolicy", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.NetworkPolicyEgressRule": { + "description": "NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8", + "properties": { + "ports": { + "description": "ports is a list of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" + }, + "type": "array" + }, + "to": { + "description": "to is a list of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPeer" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicyIngressRule": { + "description": "NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.", + "properties": { + "from": { + "description": "from is a list of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the from list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPeer" + }, + "type": "array" + }, + "ports": { + "description": "ports is a list of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicyList": { + "description": "NetworkPolicyList is a list of NetworkPolicy objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicy" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "NetworkPolicyList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "NetworkPolicyList", + "version": "v1" + } + ] + }, + "io.k8s.api.networking.v1.NetworkPolicyPeer": { + "description": "NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of fields are allowed", + "properties": { + "ipBlock": { + "$ref": "#/definitions/io.k8s.api.networking.v1.IPBlock", + "description": "ipBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be." + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "namespaceSelector selects namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf podSelector is also set, then the NetworkPolicyPeer as a whole selects the pods matching podSelector in the namespaces selected by namespaceSelector. Otherwise it selects all pods in the namespaces selected by namespaceSelector." + }, + "podSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "podSelector is a label selector which selects pods. This field follows standard label selector semantics; if present but empty, it selects all pods.\n\nIf namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects the pods matching podSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects the pods matching podSelector in the policy's own namespace." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicyPort": { + "description": "NetworkPolicyPort describes a port to allow traffic on", + "properties": { + "endPort": { + "description": "endPort indicates that the range of ports from port to endPort if set, inclusive, should be allowed by the policy. This field cannot be defined if the port field is not defined or if the port field is defined as a named (string) port. The endPort must be equal or greater than port.", + "format": "int32", + "type": "integer" + }, + "port": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "port represents the port on the given protocol. This can either be a numerical or named port on a pod. If this field is not provided, this matches all port names and numbers. If present, only traffic on the specified protocol AND port will be matched." + }, + "protocol": { + "description": "protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1.NetworkPolicySpec": { + "description": "NetworkPolicySpec provides the specification of a NetworkPolicy", + "properties": { + "egress": { + "description": "egress is a list of egress rules to be applied to the selected pods. Outgoing traffic is allowed if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic matches at least one egress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy limits all outgoing traffic (and serves solely to ensure that the pods it selects are isolated by default). This field is beta-level in 1.8", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyEgressRule" + }, + "type": "array" + }, + "ingress": { + "description": "ingress is a list of ingress rules to be applied to the selected pods. Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic source is the pod's local node, OR if the traffic matches at least one ingress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy does not allow any traffic (and serves solely to ensure that the pods it selects are isolated by default)", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyIngressRule" + }, + "type": "array" + }, + "podSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "podSelector selects the pods to which this NetworkPolicy object applies. The array of ingress rules is applied to any pods selected by this field. Multiple network policies can select the same set of pods. In this case, the ingress rules for each are combined additively. This field is NOT optional and follows standard label selector semantics. An empty podSelector matches all pods in this namespace." + }, + "policyTypes": { + "description": "policyTypes is a list of rule types that the NetworkPolicy relates to. Valid options are [\"Ingress\"], [\"Egress\"], or [\"Ingress\", \"Egress\"]. If this field is not specified, it will default based on the existence of ingress or egress rules; policies that contain an egress section are assumed to affect egress, and all policies (whether or not they contain an ingress section) are assumed to affect ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an egress section and would otherwise default to just [ \"Ingress\" ]). This field is beta-level in 1.8", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "podSelector" + ], + "type": "object" + }, + "io.k8s.api.networking.v1.ServiceBackendPort": { + "description": "ServiceBackendPort is the service port being referenced.", + "properties": { + "name": { + "description": "name is the name of the port on the Service. This is a mutually exclusive setting with \"Number\".", + "type": "string" + }, + "number": { + "description": "number is the numerical port number (e.g. 80) on the Service. This is a mutually exclusive setting with \"Name\".", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1alpha1.ClusterCIDR": { + "description": "ClusterCIDR represents a single configuration for per-Node Pod CIDR allocations when the MultiCIDRRangeAllocator is enabled (see the config for kube-controller-manager). A cluster may have any number of ClusterCIDR resources, all of which will be considered when allocating a CIDR for a Node. A ClusterCIDR is eligible to be used for a given Node when the node selector matches the node in question and has free CIDRs to allocate. In case of multiple matching ClusterCIDR resources, the allocator will attempt to break ties using internal heuristics, but any ClusterCIDR whose node selector matches the Node may be used.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterCIDR" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1alpha1.ClusterCIDRSpec", + "description": "spec is the desired state of the ClusterCIDR. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "ClusterCIDR", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.networking.v1alpha1.ClusterCIDRList": { + "description": "ClusterCIDRList contains a list of ClusterCIDR.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of ClusterCIDRs.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1alpha1.ClusterCIDR" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterCIDRList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "ClusterCIDRList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.networking.v1alpha1.ClusterCIDRSpec": { + "description": "ClusterCIDRSpec defines the desired state of ClusterCIDR.", + "properties": { + "ipv4": { + "description": "ipv4 defines an IPv4 IP block in CIDR notation(e.g. \"10.0.0.0/8\"). At least one of ipv4 and ipv6 must be specified. This field is immutable.", + "type": "string" + }, + "ipv6": { + "description": "ipv6 defines an IPv6 IP block in CIDR notation(e.g. \"2001:db8::/64\"). At least one of ipv4 and ipv6 must be specified. This field is immutable.", + "type": "string" + }, + "nodeSelector": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "nodeSelector defines which nodes the config is applicable to. An empty or nil nodeSelector selects all nodes. This field is immutable." + }, + "perNodeHostBits": { + "description": "perNodeHostBits defines the number of host bits to be configured per node. A subnet mask determines how much of the address is used for network bits and host bits. For example an IPv4 address of 192.168.0.0/24, splits the address into 24 bits for the network portion and 8 bits for the host portion. To allocate 256 IPs, set this field to 8 (a /24 mask for IPv4 or a /120 for IPv6). Minimum value is 4 (16 IPs). This field is immutable.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "perNodeHostBits" + ], + "type": "object" + }, + "io.k8s.api.networking.v1alpha1.IPAddress": { + "description": "IPAddress represents a single IP of a single IP Family. The object is designed to be used by APIs that operate on IP addresses. The object is used by the Service core API for allocation of IP addresses. An IP address can be represented in different formats, to guarantee the uniqueness of the IP, the name of the object is the IP address in canonical format, four decimal digits separated by dots suppressing leading zeros for IPv4 and the representation defined by RFC 5952 for IPv6. Valid: 192.168.1.5 or 2001:db8::1 or 2001:db8:aaaa:bbbb:cccc:dddd:eeee:1 Invalid: 10.01.2.3 or 2001:db8:0:0:0::1", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IPAddress" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1alpha1.IPAddressSpec", + "description": "spec is the desired state of the IPAddress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IPAddress", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.networking.v1alpha1.IPAddressList": { + "description": "IPAddressList contains a list of IPAddress.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of IPAddresses.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1alpha1.IPAddress" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "IPAddressList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IPAddressList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.networking.v1alpha1.IPAddressSpec": { + "description": "IPAddressSpec describe the attributes in an IP Address.", + "properties": { + "parentRef": { + "$ref": "#/definitions/io.k8s.api.networking.v1alpha1.ParentReference", + "description": "ParentRef references the resource that an IPAddress is attached to. An IPAddress must reference a parent object." + } + }, + "type": "object" + }, + "io.k8s.api.networking.v1alpha1.ParentReference": { + "description": "ParentReference describes a reference to a parent object.", + "properties": { + "group": { + "description": "Group is the group of the object being referenced.", + "type": "string" + }, + "name": { + "description": "Name is the name of the object being referenced.", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the object being referenced.", + "type": "string" + }, + "resource": { + "description": "Resource is the resource of the object being referenced.", + "type": "string" + }, + "uid": { + "description": "UID is the uid of the object being referenced.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.node.v1.Overhead": { + "description": "Overhead structure represents the resource overhead associated with running a pod.", + "properties": { + "podFixed": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "podFixed represents the fixed resource overhead associated with running a pod.", + "type": "object" + } + }, + "type": "object" + }, + "io.k8s.api.node.v1.RuntimeClass": { + "description": "RuntimeClass defines a class of container runtime supported in the cluster. The RuntimeClass is used to determine which container runtime is used to run all containers in a pod. RuntimeClasses are manually defined by a user or cluster provisioner, and referenced in the PodSpec. The Kubelet is responsible for resolving the RuntimeClassName reference before running the pod. For more details, see https://kubernetes.io/docs/concepts/containers/runtime-class/", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "handler": { + "description": "handler specifies the underlying runtime and configuration that the CRI implementation will use to handle pods of this class. The possible values are specific to the node & CRI configuration. It is assumed that all handlers are available on every node, and handlers of the same name are equivalent on every node. For example, a handler called \"runc\" might specify that the runc OCI runtime (using native Linux containers) will be used to run the containers in a pod. The Handler must be lowercase, conform to the DNS Label (RFC 1123) requirements, and is immutable.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RuntimeClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "overhead": { + "$ref": "#/definitions/io.k8s.api.node.v1.Overhead", + "description": "overhead represents the resource overhead associated with running a pod for a given RuntimeClass. For more details, see\n https://kubernetes.io/docs/concepts/scheduling-eviction/pod-overhead/" + }, + "scheduling": { + "$ref": "#/definitions/io.k8s.api.node.v1.Scheduling", + "description": "scheduling holds the scheduling constraints to ensure that pods running with this RuntimeClass are scheduled to nodes that support it. If scheduling is nil, this RuntimeClass is assumed to be supported by all nodes." + } + }, + "required": [ + "handler" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "node.k8s.io", + "kind": "RuntimeClass", + "version": "v1" + } + ] + }, + "io.k8s.api.node.v1.RuntimeClassList": { + "description": "RuntimeClassList is a list of RuntimeClass objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a list of schema objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.node.v1.RuntimeClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RuntimeClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "node.k8s.io", + "kind": "RuntimeClassList", + "version": "v1" + } + ] + }, + "io.k8s.api.node.v1.Scheduling": { + "description": "Scheduling specifies the scheduling constraints for nodes supporting a RuntimeClass.", + "properties": { + "nodeSelector": { + "additionalProperties": { + "type": "string" + }, + "description": "nodeSelector lists labels that must be present on nodes that support this RuntimeClass. Pods using this RuntimeClass can only be scheduled to a node matched by this selector. The RuntimeClass nodeSelector is merged with a pod's existing nodeSelector. Any conflicts will cause the pod to be rejected in admission.", + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "tolerations": { + "description": "tolerations are appended (excluding duplicates) to pods running with this RuntimeClass during admission, effectively unioning the set of nodes tolerated by the pod and the RuntimeClass.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Toleration" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "type": "object" + }, + "io.k8s.api.policy.v1.Eviction": { + "description": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod. A request to cause such an eviction is created by POSTing to .../pods//evictions.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "deleteOptions": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions", + "description": "DeleteOptions may be provided" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Eviction" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "ObjectMeta describes the pod that is being evicted." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "Eviction", + "version": "v1" + } + ] + }, + "io.k8s.api.policy.v1.PodDisruptionBudget": { + "description": "PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodDisruptionBudget" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.policy.v1.PodDisruptionBudgetSpec", + "description": "Specification of the desired behavior of the PodDisruptionBudget." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.policy.v1.PodDisruptionBudgetStatus", + "description": "Most recently observed status of the PodDisruptionBudget." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "PodDisruptionBudget", + "version": "v1" + } + ] + }, + "io.k8s.api.policy.v1.PodDisruptionBudgetList": { + "description": "PodDisruptionBudgetList is a collection of PodDisruptionBudgets.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of PodDisruptionBudgets", + "items": { + "$ref": "#/definitions/io.k8s.api.policy.v1.PodDisruptionBudget" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodDisruptionBudgetList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "policy", + "kind": "PodDisruptionBudgetList", + "version": "v1" + } + ] + }, + "io.k8s.api.policy.v1.PodDisruptionBudgetSpec": { + "description": "PodDisruptionBudgetSpec is a description of a PodDisruptionBudget.", + "properties": { + "maxUnavailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "An eviction is allowed if at most \"maxUnavailable\" pods selected by \"selector\" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with \"minAvailable\"." + }, + "minAvailable": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", + "description": "An eviction is allowed if at least \"minAvailable\" pods selected by \"selector\" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying \"100%\"." + }, + "selector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "Label query over pods whose evictions are managed by the disruption budget. A null selector will match no pods, while an empty ({}) selector will select all pods within the namespace.", + "x-kubernetes-patch-strategy": "replace" + }, + "unhealthyPodEvictionPolicy": { + "description": "UnhealthyPodEvictionPolicy defines the criteria for when unhealthy pods should be considered for eviction. Current implementation considers healthy pods, as pods that have status.conditions item with type=\"Ready\",status=\"True\".\n\nValid policies are IfHealthyBudget and AlwaysAllow. If no policy is specified, the default behavior will be used, which corresponds to the IfHealthyBudget policy.\n\nIfHealthyBudget policy means that running pods (status.phase=\"Running\"), but not yet healthy can be evicted only if the guarded application is not disrupted (status.currentHealthy is at least equal to status.desiredHealthy). Healthy pods will be subject to the PDB for eviction.\n\nAlwaysAllow policy means that all running pods (status.phase=\"Running\"), but not yet healthy are considered disrupted and can be evicted regardless of whether the criteria in a PDB is met. This means perspective running pods of a disrupted application might not get a chance to become healthy. Healthy pods will be subject to the PDB for eviction.\n\nAdditional policies may be added in the future. Clients making eviction decisions should disallow eviction of unhealthy pods if they encounter an unrecognized policy in this field.\n\nThis field is beta-level. The eviction API uses this field when the feature gate PDBUnhealthyPodEvictionPolicy is enabled (enabled by default).", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.policy.v1.PodDisruptionBudgetStatus": { + "description": "PodDisruptionBudgetStatus represents information about the status of a PodDisruptionBudget. Status may trail the actual state of a system.", + "properties": { + "conditions": { + "description": "Conditions contain conditions for PDB. The disruption controller sets the DisruptionAllowed condition. The following are known values for the reason field (additional reasons could be added in the future): - SyncFailed: The controller encountered an error and wasn't able to compute\n the number of allowed disruptions. Therefore no disruptions are\n allowed and the status of the condition will be False.\n- InsufficientPods: The number of pods are either at or below the number\n required by the PodDisruptionBudget. No disruptions are\n allowed and the status of the condition will be False.\n- SufficientPods: There are more pods than required by the PodDisruptionBudget.\n The condition will be True, and the number of allowed\n disruptions are provided by the disruptionsAllowed property.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "currentHealthy": { + "description": "current number of healthy pods", + "format": "int32", + "type": "integer" + }, + "desiredHealthy": { + "description": "minimum desired number of healthy pods", + "format": "int32", + "type": "integer" + }, + "disruptedPods": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "description": "DisruptedPods contains information about pods whose eviction was processed by the API server eviction subresource handler but has not yet been observed by the PodDisruptionBudget controller. A pod will be in this map from the time when the API server processed the eviction request to the time when the pod is seen by PDB controller as having been marked for deletion (or after a timeout). The key in the map is the name of the pod and the value is the time when the API server processed the eviction request. If the deletion didn't occur and a pod is still there it will be removed from the list automatically by PodDisruptionBudget controller after some time. If everything goes smooth this map should be empty for the most of the time. Large number of entries in the map may indicate problems with pod deletions.", + "type": "object" + }, + "disruptionsAllowed": { + "description": "Number of pod disruptions that are currently allowed.", + "format": "int32", + "type": "integer" + }, + "expectedPods": { + "description": "total number of pods counted by this disruption budget", + "format": "int32", + "type": "integer" + }, + "observedGeneration": { + "description": "Most recent generation observed when updating this PDB status. DisruptionsAllowed and other status information is valid only if observedGeneration equals to PDB's object generation.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "disruptionsAllowed", + "currentHealthy", + "desiredHealthy", + "expectedPods" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1.AggregationRule": { + "description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole", + "properties": { + "clusterRoleSelectors": { + "description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.api.rbac.v1.ClusterRole": { + "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", + "properties": { + "aggregationRule": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.AggregationRule", + "description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller." + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRole" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this ClusterRole", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.ClusterRoleBinding": { + "description": "ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, and adds who information via Subject.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.RoleRef", + "description": "RoleRef can only reference a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error. This field is immutable." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBinding", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.ClusterRoleBindingList": { + "description": "ClusterRoleBindingList is a collection of ClusterRoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.ClusterRoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleBindingList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.ClusterRoleList": { + "description": "ClusterRoleList is a collection of ClusterRoles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of ClusterRoles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.ClusterRole" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ClusterRoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "ClusterRoleList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.PolicyRule": { + "description": "PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to.", + "properties": { + "apiGroups": { + "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed. \"\" represents the core API group and \"*\" represents all API groups.", + "items": { + "type": "string" + }, + "type": "array" + }, + "nonResourceURLs": { + "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as \"pods\" or \"secrets\") or non-resource URL paths (such as \"/api\"), but not both.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resourceNames": { + "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "resources": { + "description": "Resources is a list of resources this rule applies to. '*' represents all resources.", + "items": { + "type": "string" + }, + "type": "array" + }, + "verbs": { + "description": "Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "verbs" + ], + "type": "object" + }, + "io.k8s.api.rbac.v1.Role": { + "description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Role" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "rules": { + "description": "Rules holds all the PolicyRules for this Role", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.PolicyRule" + }, + "type": "array" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "Role", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleBinding": { + "description": "RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given namespace only have effect in that namespace.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBinding" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata." + }, + "roleRef": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.RoleRef", + "description": "RoleRef can reference a Role in the current namespace or a ClusterRole in the global namespace. If the RoleRef cannot be resolved, the Authorizer must return an error. This field is immutable." + }, + "subjects": { + "description": "Subjects holds references to the objects the role applies to.", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.Subject" + }, + "type": "array" + } + }, + "required": [ + "roleRef" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBinding", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleBindingList": { + "description": "RoleBindingList is a collection of RoleBindings", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of RoleBindings", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.RoleBinding" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleBindingList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleBindingList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleList": { + "description": "RoleList is a collection of Roles", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is a list of Roles", + "items": { + "$ref": "#/definitions/io.k8s.api.rbac.v1.Role" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "RoleList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "rbac.authorization.k8s.io", + "kind": "RoleList", + "version": "v1" + } + ] + }, + "io.k8s.api.rbac.v1.RoleRef": { + "description": "RoleRef contains information that points to the role being used", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced", + "type": "string" + } + }, + "required": [ + "apiGroup", + "kind", + "name" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.rbac.v1.Subject": { + "description": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.", + "properties": { + "apiGroup": { + "description": "APIGroup holds the API group of the referenced subject. Defaults to \"\" for ServiceAccount subjects. Defaults to \"rbac.authorization.k8s.io\" for User and Group subjects.", + "type": "string" + }, + "kind": { + "description": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.", + "type": "string" + }, + "name": { + "description": "Name of the object being referenced.", + "type": "string" + }, + "namespace": { + "description": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.api.resource.v1alpha2.AllocationResult": { + "description": "AllocationResult contains attributes of an allocated resource.", + "properties": { + "availableOnNodes": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "This field will get set by the resource driver after it has allocated the resource to inform the scheduler where it can schedule Pods using the ResourceClaim.\n\nSetting this field is optional. If null, the resource is available everywhere." + }, + "resourceHandles": { + "description": "ResourceHandles contain the state associated with an allocation that should be maintained throughout the lifetime of a claim. Each ResourceHandle contains data that should be passed to a specific kubelet plugin once it lands on a node. This data is returned by the driver after a successful allocation and is opaque to Kubernetes. Driver documentation may explain to users how to interpret this data if needed.\n\nSetting this field is optional. It has a maximum size of 32 entries. If null (or empty), it is assumed this allocation will be processed by a single kubelet plugin with no ResourceHandle data attached. The name of the kubelet plugin invoked will match the DriverName set in the ResourceClaimStatus this AllocationResult is embedded in.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceHandle" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "shareable": { + "description": "Shareable determines whether the resource supports more than one consumer at a time.", + "type": "boolean" + } + }, + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.PodSchedulingContext": { + "description": "PodSchedulingContext objects hold information that is needed to schedule a Pod with ResourceClaims that use \"WaitForFirstConsumer\" allocation mode.\n\nThis is an alpha type and requires enabling the DynamicResourceAllocation feature gate.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodSchedulingContext" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.PodSchedulingContextSpec", + "description": "Spec describes where resources for the Pod are needed." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.PodSchedulingContextStatus", + "description": "Status describes where resources for the Pod can be allocated." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "PodSchedulingContext", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.PodSchedulingContextList": { + "description": "PodSchedulingContextList is a collection of Pod scheduling objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of PodSchedulingContext objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.PodSchedulingContext" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PodSchedulingContextList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "PodSchedulingContextList", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.PodSchedulingContextSpec": { + "description": "PodSchedulingContextSpec describes where resources for the Pod are needed.", + "properties": { + "potentialNodes": { + "description": "PotentialNodes lists nodes where the Pod might be able to run.\n\nThe size of this field is limited to 128. This is large enough for many clusters. Larger clusters may need more attempts to find a node that suits all pending resources. This may get increased in the future, but not reduced.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "selectedNode": { + "description": "SelectedNode is the node for which allocation of ResourceClaims that are referenced by the Pod and that use \"WaitForFirstConsumer\" allocation is to be attempted.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.PodSchedulingContextStatus": { + "description": "PodSchedulingContextStatus describes where resources for the Pod can be allocated.", + "properties": { + "resourceClaims": { + "description": "ResourceClaims describes resource availability for each pod.spec.resourceClaim entry where the corresponding ResourceClaim uses \"WaitForFirstConsumer\" allocation mode.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimSchedulingStatus" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + } + }, + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClaim": { + "description": "ResourceClaim describes which resources are needed by a resource consumer. Its status tracks whether the resource has been allocated and what the resulting attributes are.\n\nThis is an alpha type and requires enabling the DynamicResourceAllocation feature gate.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceClaim" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimSpec", + "description": "Spec describes the desired attributes of a resource that then needs to be allocated. It can only be set once when creating the ResourceClaim." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimStatus", + "description": "Status describes whether the resource is available and with which attributes." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "ResourceClaim", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimConsumerReference": { + "description": "ResourceClaimConsumerReference contains enough information to let you locate the consumer of a ResourceClaim. The user must be a resource in the same namespace as the ResourceClaim.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. It is empty for the core API. This matches the group in the APIVersion that is used when creating the resources.", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced.", + "type": "string" + }, + "resource": { + "description": "Resource is the type of resource being referenced, for example \"pods\".", + "type": "string" + }, + "uid": { + "description": "UID identifies exactly one incarnation of the resource.", + "type": "string" + } + }, + "required": [ + "resource", + "name", + "uid" + ], + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimList": { + "description": "ResourceClaimList is a collection of claims.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of resource claims.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaim" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceClaimList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "ResourceClaimList", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimParametersReference": { + "description": "ResourceClaimParametersReference contains enough information to let you locate the parameters for a ResourceClaim. The object must be in the same namespace as the ResourceClaim.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. It is empty for the core API. This matches the group in the APIVersion that is used when creating the resources.", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced. This is the same value as in the parameter object's metadata, for example \"ConfigMap\".", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimSchedulingStatus": { + "description": "ResourceClaimSchedulingStatus contains information about one particular ResourceClaim with \"WaitForFirstConsumer\" allocation mode.", + "properties": { + "name": { + "description": "Name matches the pod.spec.resourceClaims[*].Name field.", + "type": "string" + }, + "unsuitableNodes": { + "description": "UnsuitableNodes lists nodes that the ResourceClaim cannot be allocated for.\n\nThe size of this field is limited to 128, the same as for PodSchedulingSpec.PotentialNodes. This may get increased in the future, but not reduced.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimSpec": { + "description": "ResourceClaimSpec defines how a resource is to be allocated.", + "properties": { + "allocationMode": { + "description": "Allocation can start immediately or when a Pod wants to use the resource. \"WaitForFirstConsumer\" is the default.", + "type": "string" + }, + "parametersRef": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimParametersReference", + "description": "ParametersRef references a separate object with arbitrary parameters that will be used by the driver when allocating a resource for the claim.\n\nThe object must be in the same namespace as the ResourceClaim." + }, + "resourceClassName": { + "description": "ResourceClassName references the driver and additional parameters via the name of a ResourceClass that was created as part of the driver deployment.", + "type": "string" + } + }, + "required": [ + "resourceClassName" + ], + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimStatus": { + "description": "ResourceClaimStatus tracks whether the resource has been allocated and what the resulting attributes are.", + "properties": { + "allocation": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.AllocationResult", + "description": "Allocation is set by the resource driver once a resource or set of resources has been allocated successfully. If this is not specified, the resources have not been allocated yet." + }, + "deallocationRequested": { + "description": "DeallocationRequested indicates that a ResourceClaim is to be deallocated.\n\nThe driver then must deallocate this claim and reset the field together with clearing the Allocation field.\n\nWhile DeallocationRequested is set, no new consumers may be added to ReservedFor.", + "type": "boolean" + }, + "driverName": { + "description": "DriverName is a copy of the driver name from the ResourceClass at the time when allocation started.", + "type": "string" + }, + "reservedFor": { + "description": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimConsumerReference" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "uid" + ], + "x-kubernetes-list-type": "map" + } + }, + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimTemplate": { + "description": "ResourceClaimTemplate is used to produce ResourceClaim objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceClaimTemplate" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimTemplateSpec", + "description": "Describes the ResourceClaim that is to be generated.\n\nThis field is immutable. A ResourceClaim will get created by the control plane for a Pod when needed and then not get updated anymore." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "ResourceClaimTemplate", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimTemplateList": { + "description": "ResourceClaimTemplateList is a collection of claim templates.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of resource claim templates.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimTemplate" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceClaimTemplateList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "ResourceClaimTemplateList", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.ResourceClaimTemplateSpec": { + "description": "ResourceClaimTemplateSpec contains the metadata and fields for a ResourceClaim.", + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "ObjectMeta may contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClaimSpec", + "description": "Spec for the ResourceClaim. The entire content is copied unchanged into the ResourceClaim that gets created from this template. The same fields as in a ResourceClaim are also valid here." + } + }, + "required": [ + "spec" + ], + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceClass": { + "description": "ResourceClass is used by administrators to influence how resources are allocated.\n\nThis is an alpha type and requires enabling the DynamicResourceAllocation feature gate.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "driverName": { + "description": "DriverName defines the name of the dynamic resource driver that is used for allocation of a ResourceClaim that uses this class.\n\nResource drivers have a unique name in forward domain order (acme.example.com).", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata" + }, + "parametersRef": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClassParametersReference", + "description": "ParametersRef references an arbitrary separate object that may hold parameters that will be used by the driver when allocating a resource that uses this class. A dynamic resource driver can distinguish between parameters stored here and and those stored in ResourceClaimSpec." + }, + "suitableNodes": { + "$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector", + "description": "Only nodes matching the selector will be considered by the scheduler when trying to find a Node that fits a Pod when that Pod uses a ResourceClaim that has not been allocated yet.\n\nSetting this field is optional. If null, all nodes are candidates." + } + }, + "required": [ + "driverName" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "ResourceClass", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.ResourceClassList": { + "description": "ResourceClassList is a collection of classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of resource classes.", + "items": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha2.ResourceClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "ResourceClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "resource.k8s.io", + "kind": "ResourceClassList", + "version": "v1alpha2" + } + ] + }, + "io.k8s.api.resource.v1alpha2.ResourceClassParametersReference": { + "description": "ResourceClassParametersReference contains enough information to let you locate the parameters for a ResourceClass.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. It is empty for the core API. This matches the group in the APIVersion that is used when creating the resources.", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced. This is the same value as in the parameter object's metadata.", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced.", + "type": "string" + }, + "namespace": { + "description": "Namespace that contains the referenced resource. Must be empty for cluster-scoped resources and non-empty for namespaced resources.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.resource.v1alpha2.ResourceHandle": { + "description": "ResourceHandle holds opaque resource data for processing by a specific kubelet plugin.", + "properties": { + "data": { + "description": "Data contains the opaque data associated with this ResourceHandle. It is set by the controller component of the resource driver whose name matches the DriverName set in the ResourceClaimStatus this ResourceHandle is embedded in. It is set at allocation time and is intended for processing by the kubelet plugin whose name matches the DriverName set in this ResourceHandle.\n\nThe maximum size of this field is 16KiB. This may get increased in the future, but not reduced.", + "type": "string" + }, + "driverName": { + "description": "DriverName specifies the name of the resource driver whose kubelet plugin should be invoked to process this ResourceHandle's data once it lands on a node. This may differ from the DriverName set in ResourceClaimStatus this ResourceHandle is embedded in.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.scheduling.v1.PriorityClass": { + "description": "PriorityClass defines mapping from a priority class name to the priority integer value. The value can be any valid integer.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "description": { + "description": "description is an arbitrary string that usually provides guidelines on when this priority class should be used.", + "type": "string" + }, + "globalDefault": { + "description": "globalDefault specifies whether this PriorityClass should be considered as the default priority for pods that do not have any priority class. Only one PriorityClass can be marked as `globalDefault`. However, if more than one PriorityClasses exists with their `globalDefault` field set to true, the smallest value of such global default PriorityClasses will be used as the default priority.", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "preemptionPolicy": { + "description": "preemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.", + "type": "string" + }, + "value": { + "description": "value represents the integer value of this priority class. This is the actual priority that pods receive when they have the name of this class in their pod spec.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "value" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClass", + "version": "v1" + } + ] + }, + "io.k8s.api.scheduling.v1.PriorityClassList": { + "description": "PriorityClassList is a collection of priority classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of PriorityClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1.PriorityClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "PriorityClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "PriorityClassList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.CSIDriver": { + "description": "CSIDriver captures information about a Container Storage Interface (CSI) volume driver deployed on the cluster. Kubernetes attach detach controller uses this object to determine whether attach is required. Kubelet uses this object to determine whether pod information needs to be passed on mount. CSIDriver objects are non-namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSIDriver" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. metadata.Name indicates the name of the CSI driver that this object refers to; it MUST be the same name returned by the CSI GetPluginName() call for that driver. The driver name must be 63 characters or less, beginning and ending with an alphanumeric character ([a-z0-9A-Z]) with dashes (-), dots (.), and alphanumerics between. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriverSpec", + "description": "spec represents the specification of the CSI Driver." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSIDriver", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.CSIDriverList": { + "description": "CSIDriverList is a collection of CSIDriver objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CSIDriver", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSIDriverList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSIDriverList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.CSIDriverSpec": { + "description": "CSIDriverSpec is the specification of a CSIDriver.", + "properties": { + "attachRequired": { + "description": "attachRequired indicates this CSI volume driver requires an attach operation (because it implements the CSI ControllerPublishVolume() method), and that the Kubernetes attach detach controller should call the attach volume interface which checks the volumeattachment status and waits until the volume is attached before proceeding to mounting. The CSI external-attacher coordinates with CSI volume driver and updates the volumeattachment status when the attach operation is complete. If the CSIDriverRegistry feature gate is enabled and the value is specified to false, the attach operation will be skipped. Otherwise the attach operation will be called.\n\nThis field is immutable.", + "type": "boolean" + }, + "fsGroupPolicy": { + "description": "fsGroupPolicy defines if the underlying volume supports changing ownership and permission of the volume before being mounted. Refer to the specific FSGroupPolicy values for additional details.\n\nThis field is immutable.\n\nDefaults to ReadWriteOnceWithFSType, which will examine each volume to determine if Kubernetes should modify ownership and permissions of the volume. With the default policy the defined fsGroup will only be applied if a fstype is defined and the volume's access mode contains ReadWriteOnce.", + "type": "string" + }, + "podInfoOnMount": { + "description": "podInfoOnMount indicates this CSI volume driver requires additional pod information (like podName, podUID, etc.) during mount operations, if set to true. If set to false, pod information will not be passed on mount. Default is false.\n\nThe CSI driver specifies podInfoOnMount as part of driver deployment. If true, Kubelet will pass pod information as VolumeContext in the CSI NodePublishVolume() calls. The CSI driver is responsible for parsing and validating the information passed in as VolumeContext.\n\nThe following VolumeConext will be passed if podInfoOnMount is set to true. This list might grow, but the prefix will be used. \"csi.storage.k8s.io/pod.name\": pod.Name \"csi.storage.k8s.io/pod.namespace\": pod.Namespace \"csi.storage.k8s.io/pod.uid\": string(pod.UID) \"csi.storage.k8s.io/ephemeral\": \"true\" if the volume is an ephemeral inline volume\n defined by a CSIVolumeSource, otherwise \"false\"\n\n\"csi.storage.k8s.io/ephemeral\" is a new feature in Kubernetes 1.16. It is only required for drivers which support both the \"Persistent\" and \"Ephemeral\" VolumeLifecycleMode. Other drivers can leave pod info disabled and/or ignore this field. As Kubernetes 1.15 doesn't support this field, drivers can only support one mode when deployed on such a cluster and the deployment determines which mode that is, for example via a command line parameter of the driver.\n\nThis field is immutable.", + "type": "boolean" + }, + "requiresRepublish": { + "description": "requiresRepublish indicates the CSI driver wants `NodePublishVolume` being periodically called to reflect any possible change in the mounted volume. This field defaults to false.\n\nNote: After a successful initial NodePublishVolume call, subsequent calls to NodePublishVolume should only update the contents of the volume. New mount points will not be seen by a running container.", + "type": "boolean" + }, + "seLinuxMount": { + "description": "seLinuxMount specifies if the CSI driver supports \"-o context\" mount option.\n\nWhen \"true\", the CSI driver must ensure that all volumes provided by this CSI driver can be mounted separately with different `-o context` options. This is typical for storage backends that provide volumes as filesystems on block devices or as independent shared volumes. Kubernetes will call NodeStage / NodePublish with \"-o context=xyz\" mount option when mounting a ReadWriteOncePod volume used in Pod that has explicitly set SELinux context. In the future, it may be expanded to other volume AccessModes. In any case, Kubernetes will ensure that the volume is mounted only with a single SELinux context.\n\nWhen \"false\", Kubernetes won't pass any special SELinux mount options to the driver. This is typical for volumes that represent subdirectories of a bigger shared filesystem.\n\nDefault is \"false\".", + "type": "boolean" + }, + "storageCapacity": { + "description": "storageCapacity indicates that the CSI volume driver wants pod scheduling to consider the storage capacity that the driver deployment will report by creating CSIStorageCapacity objects with capacity information, if set to true.\n\nThe check can be enabled immediately when deploying a driver. In that case, provisioning new volumes with late binding will pause until the driver deployment has published some suitable CSIStorageCapacity object.\n\nAlternatively, the driver can be deployed with the field unset or false and it can be flipped later when storage capacity information has been published.\n\nThis field was immutable in Kubernetes <= 1.22 and now is mutable.", + "type": "boolean" + }, + "tokenRequests": { + "description": "tokenRequests indicates the CSI driver needs pods' service account tokens it is mounting volume for to do necessary authentication. Kubelet will pass the tokens in VolumeContext in the CSI NodePublishVolume calls. The CSI driver should parse and validate the following VolumeContext: \"csi.storage.k8s.io/serviceAccount.tokens\": {\n \"\": {\n \"token\": ,\n \"expirationTimestamp\": ,\n },\n ...\n}\n\nNote: Audience in each TokenRequest should be different and at most one token is empty string. To receive a new token after expiry, RequiresRepublish can be used to trigger NodePublishVolume periodically.", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.TokenRequest" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "volumeLifecycleModes": { + "description": "volumeLifecycleModes defines what kind of volumes this CSI volume driver supports. The default if the list is empty is \"Persistent\", which is the usage defined by the CSI specification and implemented in Kubernetes via the usual PV/PVC mechanism.\n\nThe other mode is \"Ephemeral\". In this mode, volumes are defined inline inside the pod spec with CSIVolumeSource and their lifecycle is tied to the lifecycle of that pod. A driver has to be aware of this because it is only going to get a NodePublishVolume call for such a volume.\n\nFor more information about implementing this mode, see https://kubernetes-csi.github.io/docs/ephemeral-local-volumes.html A driver can support one or more of these modes and more modes may be added in the future.\n\nThis field is beta. This field is immutable.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1.CSINode": { + "description": "CSINode holds information about all CSI drivers installed on a node. CSI drivers do not need to create the CSINode object directly. As long as they use the node-driver-registrar sidecar container, the kubelet will automatically populate the CSINode object for the CSI driver as part of kubelet plugin registration. CSINode has the same name as a node. If the object is missing, it means either there are no CSI Drivers available on the node, or the Kubelet version is low enough that it doesn't create this object. CSINode has an OwnerReference that points to the corresponding node object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSINode" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. metadata.name must be the Kubernetes node name." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINodeSpec", + "description": "spec is the specification of CSINode" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSINode", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.CSINodeDriver": { + "description": "CSINodeDriver holds information about the specification of one CSI driver installed on a node", + "properties": { + "allocatable": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeNodeResources", + "description": "allocatable represents the volume resources of a node that are available for scheduling. This field is beta." + }, + "name": { + "description": "name represents the name of the CSI driver that this object refers to. This MUST be the same name returned by the CSI GetPluginName() call for that driver.", + "type": "string" + }, + "nodeID": { + "description": "nodeID of the node from the driver point of view. This field enables Kubernetes to communicate with storage systems that do not share the same nomenclature for nodes. For example, Kubernetes may refer to a given node as \"node1\", but the storage system may refer to the same node as \"nodeA\". When Kubernetes issues a command to the storage system to attach a volume to a specific node, it can use this field to refer to the node name using the ID that the storage system will understand, e.g. \"nodeA\" instead of \"node1\". This field is required.", + "type": "string" + }, + "topologyKeys": { + "description": "topologyKeys is the list of keys supported by the driver. When a driver is initialized on a cluster, it provides a set of topology keys that it understands (e.g. \"company.com/zone\", \"company.com/region\"). When a driver is initialized on a node, it provides the same topology keys along with values. Kubelet will expose these topology keys as labels on its own node object. When Kubernetes does topology aware provisioning, it can use this list to determine which labels it should retrieve from the node object and pass back to the driver. It is possible for different nodes to use different topology keys. This can be empty if driver does not support topology.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "nodeID" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.CSINodeList": { + "description": "CSINodeList is a collection of CSINode objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CSINode", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSINodeList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSINodeList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.CSINodeSpec": { + "description": "CSINodeSpec holds information about the specification of all CSI drivers installed on a node", + "properties": { + "drivers": { + "description": "drivers is a list of information of all CSI Drivers existing on a node. If all drivers in the list are uninstalled, this can become empty.", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINodeDriver" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "required": [ + "drivers" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.CSIStorageCapacity": { + "description": "CSIStorageCapacity stores the result of one CSI GetCapacity call. For a given StorageClass, this describes the available capacity in a particular topology segment. This can be used when considering where to instantiate new PersistentVolumes.\n\nFor example this can express things like: - StorageClass \"standard\" has \"1234 GiB\" available in \"topology.kubernetes.io/zone=us-east1\" - StorageClass \"localssd\" has \"10 GiB\" available in \"kubernetes.io/hostname=knode-abc123\"\n\nThe following three cases all imply that no capacity is available for a certain combination: - no object exists with suitable topology and storage class name - such an object exists, but the capacity is unset - such an object exists, but the capacity is zero\n\nThe producer of these objects can decide which approach is more suitable.\n\nThey are consumed by the kube-scheduler when a CSI driver opts into capacity-aware scheduling with CSIDriverSpec.StorageCapacity. The scheduler compares the MaximumVolumeSize against the requested size of pending volumes to filter out unsuitable nodes. If MaximumVolumeSize is unset, it falls back to a comparison against the less precise Capacity. If that is also unset, the scheduler assumes that capacity is insufficient and tries some other node.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "capacity": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "capacity is the value reported by the CSI driver in its GetCapacityResponse for a GetCapacityRequest with topology and parameters that match the previous fields.\n\nThe semantic is currently (CSI spec 1.2) defined as: The available capacity, in bytes, of the storage that can be used to provision volumes. If not set, that information is currently unavailable." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSIStorageCapacity" + ] + }, + "maximumVolumeSize": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", + "description": "maximumVolumeSize is the value reported by the CSI driver in its GetCapacityResponse for a GetCapacityRequest with topology and parameters that match the previous fields.\n\nThis is defined since CSI spec 1.4.0 as the largest size that may be used in a CreateVolumeRequest.capacity_range.required_bytes field to create a volume with the same parameters as those in GetCapacityRequest. The corresponding value in the Kubernetes API is ResourceRequirements.Requests in a volume claim." + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. The name has no particular meaning. It must be a DNS subdomain (dots allowed, 253 characters). To ensure that there are no conflicts with other CSI drivers on the cluster, the recommendation is to use csisc-, a generated name, or a reverse-domain name which ends with the unique CSI driver name.\n\nObjects are namespaced.\n\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "nodeTopology": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "nodeTopology defines which nodes have access to the storage for which capacity was reported. If not set, the storage is not accessible from any node in the cluster. If empty, the storage is accessible from all nodes. This field is immutable." + }, + "storageClassName": { + "description": "storageClassName represents the name of the StorageClass that the reported capacity applies to. It must meet the same requirements as the name of a StorageClass object (non-empty, DNS subdomain). If that object no longer exists, the CSIStorageCapacity object is obsolete and should be removed by its creator. This field is immutable.", + "type": "string" + } + }, + "required": [ + "storageClassName" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSIStorageCapacity", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.CSIStorageCapacityList": { + "description": "CSIStorageCapacityList is a collection of CSIStorageCapacity objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of CSIStorageCapacity objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CSIStorageCapacityList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "CSIStorageCapacityList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.StorageClass": { + "description": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.", + "properties": { + "allowVolumeExpansion": { + "description": "allowVolumeExpansion shows whether the storage class allow volume expand.", + "type": "boolean" + }, + "allowedTopologies": { + "description": "allowedTopologies restrict the node topologies where volumes can be dynamically provisioned. Each volume plugin defines its own supported topology specifications. An empty TopologySelectorTerm list means there is no topology restriction. This field is only honored by servers that enable the VolumeScheduling feature.", + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.TopologySelectorTerm" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageClass" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "mountOptions": { + "description": "mountOptions controls the mountOptions for dynamically provisioned PersistentVolumes of this storage class. e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.", + "items": { + "type": "string" + }, + "type": "array" + }, + "parameters": { + "additionalProperties": { + "type": "string" + }, + "description": "parameters holds the parameters for the provisioner that should create volumes of this storage class.", + "type": "object" + }, + "provisioner": { + "description": "provisioner indicates the type of the provisioner.", + "type": "string" + }, + "reclaimPolicy": { + "description": "reclaimPolicy controls the reclaimPolicy for dynamically provisioned PersistentVolumes of this storage class. Defaults to Delete.", + "type": "string" + }, + "volumeBindingMode": { + "description": "volumeBindingMode indicates how PersistentVolumeClaims should be provisioned and bound. When unset, VolumeBindingImmediate is used. This field is only honored by servers that enable the VolumeScheduling feature.", + "type": "string" + } + }, + "required": [ + "provisioner" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "StorageClass", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.StorageClassList": { + "description": "StorageClassList is a collection of storage classes.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of StorageClasses", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "StorageClassList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "StorageClassList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.TokenRequest": { + "description": "TokenRequest contains parameters of a service account token.", + "properties": { + "audience": { + "description": "audience is the intended audience of the token in \"TokenRequestSpec\". It will default to the audiences of kube apiserver.", + "type": "string" + }, + "expirationSeconds": { + "description": "expirationSeconds is the duration of validity of the token in \"TokenRequestSpec\". It has the same default value of \"ExpirationSeconds\" in \"TokenRequestSpec\".", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "audience" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeAttachment": { + "description": "VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.\n\nVolumeAttachment objects are non-namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachment" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentSpec", + "description": "spec represents specification of the desired attach/detach volume behavior. Populated by the Kubernetes system." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentStatus", + "description": "status represents status of the VolumeAttachment request. Populated by the entity completing the attach or detach operation, i.e. the external-attacher." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachment", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.VolumeAttachmentList": { + "description": "VolumeAttachmentList is a collection of VolumeAttachment objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is the list of VolumeAttachments", + "items": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "VolumeAttachmentList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storage.k8s.io", + "kind": "VolumeAttachmentList", + "version": "v1" + } + ] + }, + "io.k8s.api.storage.v1.VolumeAttachmentSource": { + "description": "VolumeAttachmentSource represents a volume that should be attached. Right now only PersistenVolumes can be attached via external attacher, in future we may allow also inline volumes in pods. Exactly one member can be set.", + "properties": { + "inlineVolumeSpec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeSpec", + "description": "inlineVolumeSpec contains all the information necessary to attach a persistent volume defined by a pod's inline VolumeSource. This field is populated only for the CSIMigration feature. It contains translated fields from a pod's inline VolumeSource to a PersistentVolumeSpec. This field is beta-level and is only honored by servers that enabled the CSIMigration feature." + }, + "persistentVolumeName": { + "description": "persistentVolumeName represents the name of the persistent volume to attach.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeAttachmentSpec": { + "description": "VolumeAttachmentSpec is the specification of a VolumeAttachment request.", + "properties": { + "attacher": { + "description": "attacher indicates the name of the volume driver that MUST handle this request. This is the name returned by GetPluginName().", + "type": "string" + }, + "nodeName": { + "description": "nodeName represents the node that the volume should be attached to.", + "type": "string" + }, + "source": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentSource", + "description": "source represents the volume that should be attached." + } + }, + "required": [ + "attacher", + "source", + "nodeName" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeAttachmentStatus": { + "description": "VolumeAttachmentStatus is the status of a VolumeAttachment request.", + "properties": { + "attachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeError", + "description": "attachError represents the last error encountered during attach operation, if any. This field must only be set by the entity completing the attach operation, i.e. the external-attacher." + }, + "attached": { + "description": "attached indicates the volume is successfully attached. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "boolean" + }, + "attachmentMetadata": { + "additionalProperties": { + "type": "string" + }, + "description": "attachmentMetadata is populated with any information returned by the attach operation, upon successful attach, that must be passed into subsequent WaitForAttach or Mount calls. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.", + "type": "object" + }, + "detachError": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeError", + "description": "detachError represents the last error encountered during detach operation, if any. This field must only be set by the entity completing the detach operation, i.e. the external-attacher." + } + }, + "required": [ + "attached" + ], + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeError": { + "description": "VolumeError captures an error encountered during a volume operation.", + "properties": { + "message": { + "description": "message represents the error encountered during Attach or Detach operation. This string may be logged, so it should not contain sensitive information.", + "type": "string" + }, + "time": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "time represents the time the error was encountered." + } + }, + "type": "object" + }, + "io.k8s.api.storage.v1.VolumeNodeResources": { + "description": "VolumeNodeResources is a set of resource limits for scheduling of volumes.", + "properties": { + "count": { + "description": "count indicates the maximum number of unique volumes managed by the CSI driver that can be used on a node. A volume that is both attached and mounted on a node is considered to be used once, not twice. The same rule applies for a unique volume that is shared among multiple pods on the same node. If this field is not specified, then the supported number of volumes on this node is unbounded.", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceColumnDefinition": { + "description": "CustomResourceColumnDefinition specifies a column for server side printing.", + "properties": { + "description": { + "description": "description is a human readable description of this column.", + "type": "string" + }, + "format": { + "description": "format is an optional OpenAPI type definition for this column. The 'name' format is applied to the primary identifier column to assist in clients identifying column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for details.", + "type": "string" + }, + "jsonPath": { + "description": "jsonPath is a simple JSON path (i.e. with array notation) which is evaluated against each custom resource to produce the value for this column.", + "type": "string" + }, + "name": { + "description": "name is a human readable name for the column.", + "type": "string" + }, + "priority": { + "description": "priority is an integer defining the relative importance of this column compared to others. Lower numbers are considered higher priority. Columns that may be omitted in limited space scenarios should be given a priority greater than 0.", + "format": "int32", + "type": "integer" + }, + "type": { + "description": "type is an OpenAPI type definition for this column. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for details.", + "type": "string" + } + }, + "required": [ + "name", + "type", + "jsonPath" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceConversion": { + "description": "CustomResourceConversion describes how to convert different versions of a CR.", + "properties": { + "strategy": { + "description": "strategy specifies how custom resources are converted between versions. Allowed values are: - `\"None\"`: The converter only change the apiVersion and would not touch any other field in the custom resource. - `\"Webhook\"`: API Server will call to an external webhook to do the conversion. Additional information\n is needed for this option. This requires spec.preserveUnknownFields to be false, and spec.conversion.webhook to be set.", + "type": "string" + }, + "webhook": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.WebhookConversion", + "description": "webhook describes how to call the conversion webhook. Required when `strategy` is set to `\"Webhook\"`." + } + }, + "required": [ + "strategy" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinition": { + "description": "CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format <.spec.name>.<.spec.group>.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CustomResourceDefinition" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionSpec", + "description": "spec describes how the user wants the resources to appear" + }, + "status": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionStatus", + "description": "status indicates the actual state of the CustomResourceDefinition" + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiextensions.k8s.io", + "kind": "CustomResourceDefinition", + "version": "v1" + } + ] + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionCondition": { + "description": "CustomResourceDefinitionCondition contains details for the current condition of this pod.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime last time the condition transitioned from one status to another." + }, + "message": { + "description": "message is a human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "reason is a unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "status is the status of the condition. Can be True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "type is the type of the condition. Types include Established, NamesAccepted and Terminating.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionList": { + "description": "CustomResourceDefinitionList is a list of CustomResourceDefinition objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items list individual CustomResourceDefinition objects", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinition" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "CustomResourceDefinitionList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiextensions.k8s.io", + "kind": "CustomResourceDefinitionList", + "version": "v1" + } + ] + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionNames": { + "description": "CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition", + "properties": { + "categories": { + "description": "categories is a list of grouped resources this custom resource belongs to (e.g. 'all'). This is published in API discovery documents, and used by clients to support invocations like `kubectl get all`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "kind": { + "description": "kind is the serialized kind of the resource. It is normally CamelCase and singular. Custom resource instances will use this value as the `kind` attribute in API calls.", + "type": "string" + }, + "listKind": { + "description": "listKind is the serialized kind of the list for this resource. Defaults to \"`kind`List\".", + "type": "string" + }, + "plural": { + "description": "plural is the plural name of the resource to serve. The custom resources are served under `/apis///.../`. Must match the name of the CustomResourceDefinition (in the form `.`). Must be all lowercase.", + "type": "string" + }, + "shortNames": { + "description": "shortNames are short names for the resource, exposed in API discovery documents, and used by clients to support invocations like `kubectl get `. It must be all lowercase.", + "items": { + "type": "string" + }, + "type": "array" + }, + "singular": { + "description": "singular is the singular name of the resource. It must be all lowercase. Defaults to lowercased `kind`.", + "type": "string" + } + }, + "required": [ + "plural", + "kind" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionSpec": { + "description": "CustomResourceDefinitionSpec describes how a user wants their resource to appear", + "properties": { + "conversion": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceConversion", + "description": "conversion defines conversion settings for the CRD." + }, + "group": { + "description": "group is the API group of the defined custom resource. The custom resources are served under `/apis//...`. Must match the name of the CustomResourceDefinition (in the form `.`).", + "type": "string" + }, + "names": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionNames", + "description": "names specify the resource and kind names for the custom resource." + }, + "preserveUnknownFields": { + "description": "preserveUnknownFields indicates that object fields which are not specified in the OpenAPI schema should be preserved when persisting to storage. apiVersion, kind, metadata and known fields inside metadata are always preserved. This field is deprecated in favor of setting `x-preserve-unknown-fields` to true in `spec.versions[*].schema.openAPIV3Schema`. See https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#field-pruning for details.", + "type": "boolean" + }, + "scope": { + "description": "scope indicates whether the defined custom resource is cluster- or namespace-scoped. Allowed values are `Cluster` and `Namespaced`.", + "type": "string" + }, + "versions": { + "description": "versions is the list of all API versions of the defined custom resource. Version names are used to compute the order in which served versions are listed in API discovery. If the version string is \"kube-like\", it will sort above non \"kube-like\" version strings, which are ordered lexicographically. \"Kube-like\" versions start with a \"v\", then are followed by a number (the major version), then optionally the string \"alpha\" or \"beta\" and another number (the minor version). These are sorted first by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing major version, then minor version. An example sorted list of versions: v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionVersion" + }, + "type": "array" + } + }, + "required": [ + "group", + "names", + "scope", + "versions" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionStatus": { + "description": "CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition", + "properties": { + "acceptedNames": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionNames", + "description": "acceptedNames are the names that are actually being used to serve discovery. They may be different than the names in spec." + }, + "conditions": { + "description": "conditions indicate state for particular aspects of a CustomResourceDefinition", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "storedVersions": { + "description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionVersion": { + "description": "CustomResourceDefinitionVersion describes a version for CRD.", + "properties": { + "additionalPrinterColumns": { + "description": "additionalPrinterColumns specifies additional columns returned in Table output. See https://kubernetes.io/docs/reference/using-api/api-concepts/#receiving-resources-as-tables for details. If no columns are specified, a single column displaying the age of the custom resource is used.", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceColumnDefinition" + }, + "type": "array" + }, + "deprecated": { + "description": "deprecated indicates this version of the custom resource API is deprecated. When set to true, API requests to this version receive a warning header in the server response. Defaults to false.", + "type": "boolean" + }, + "deprecationWarning": { + "description": "deprecationWarning overrides the default warning returned to API clients. May only be set when `deprecated` is true. The default warning indicates this version is deprecated and recommends use of the newest served version of equal or greater stability, if one exists.", + "type": "string" + }, + "name": { + "description": "name is the version name, e.g. “v1”, “v2beta1”, etc. The custom resources are served under this version at `/apis///...` if `served` is true.", + "type": "string" + }, + "schema": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceValidation", + "description": "schema describes the schema used for validation, pruning, and defaulting of this version of the custom resource." + }, + "served": { + "description": "served is a flag enabling/disabling this version from being served via REST APIs", + "type": "boolean" + }, + "storage": { + "description": "storage indicates this version should be used when persisting custom resources to storage. There must be exactly one version with storage=true.", + "type": "boolean" + }, + "subresources": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceSubresources", + "description": "subresources specify what subresources this version of the defined custom resource have." + } + }, + "required": [ + "name", + "served", + "storage" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceSubresourceScale": { + "description": "CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.", + "properties": { + "labelSelectorPath": { + "description": "labelSelectorPath defines the JSON path inside of a custom resource that corresponds to Scale `status.selector`. Only JSON paths without the array notation are allowed. Must be a JSON Path under `.status` or `.spec`. Must be set to work with HorizontalPodAutoscaler. The field pointed by this JSON path must be a string field (not a complex selector struct) which contains a serialized label selector in string form. More info: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource If there is no value under the given path in the custom resource, the `status.selector` value in the `/scale` subresource will default to the empty string.", + "type": "string" + }, + "specReplicasPath": { + "description": "specReplicasPath defines the JSON path inside of a custom resource that corresponds to Scale `spec.replicas`. Only JSON paths without the array notation are allowed. Must be a JSON Path under `.spec`. If there is no value under the given path in the custom resource, the `/scale` subresource will return an error on GET.", + "type": "string" + }, + "statusReplicasPath": { + "description": "statusReplicasPath defines the JSON path inside of a custom resource that corresponds to Scale `status.replicas`. Only JSON paths without the array notation are allowed. Must be a JSON Path under `.status`. If there is no value under the given path in the custom resource, the `status.replicas` value in the `/scale` subresource will default to 0.", + "type": "string" + } + }, + "required": [ + "specReplicasPath", + "statusReplicasPath" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceSubresourceStatus": { + "description": "CustomResourceSubresourceStatus defines how to serve the status subresource for CustomResources. Status is represented by the `.status` JSON path inside of a CustomResource. When set, * exposes a /status subresource for the custom resource * PUT requests to the /status subresource take a custom resource object, and ignore changes to anything except the status stanza * PUT/POST/PATCH requests to the custom resource ignore changes to the status stanza", + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceSubresources": { + "description": "CustomResourceSubresources defines the status and scale subresources for CustomResources.", + "properties": { + "scale": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceSubresourceScale", + "description": "scale indicates the custom resource should serve a `/scale` subresource that returns an `autoscaling/v1` Scale object." + }, + "status": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceSubresourceStatus", + "description": "status indicates the custom resource should serve a `/status` subresource. When enabled: 1. requests to the custom resource primary endpoint ignore changes to the `status` stanza of the object. 2. requests to the custom resource `/status` subresource ignore changes to anything other than the `status` stanza of the object." + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceValidation": { + "description": "CustomResourceValidation is a list of validation methods for CustomResources.", + "properties": { + "openAPIV3Schema": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps", + "description": "openAPIV3Schema is the OpenAPI v3 schema to use for validation and pruning." + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.ExternalDocumentation": { + "description": "ExternalDocumentation allows referencing an external resource for extended documentation.", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON": { + "description": "JSON represents any valid JSON value. These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps": { + "description": "JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).", + "properties": { + "$ref": { + "type": "string" + }, + "$schema": { + "type": "string" + }, + "additionalItems": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool" + }, + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool" + }, + "allOf": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "type": "array" + }, + "anyOf": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "type": "array" + }, + "default": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON", + "description": "default is a default value for undefined object fields. Defaulting is a beta feature under the CustomResourceDefaulting feature gate. Defaulting requires spec.preserveUnknownFields to be false." + }, + "definitions": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "type": "object" + }, + "dependencies": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrStringArray" + }, + "type": "object" + }, + "description": { + "type": "string" + }, + "enum": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON" + }, + "type": "array" + }, + "example": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSON" + }, + "exclusiveMaximum": { + "type": "boolean" + }, + "exclusiveMinimum": { + "type": "boolean" + }, + "externalDocs": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.ExternalDocumentation" + }, + "format": { + "description": "format is an OpenAPI v3 format string. Unknown formats are ignored. The following formats are validated:\n\n- bsonobjectid: a bson object ID, i.e. a 24 characters hex string - uri: an URI as parsed by Golang net/url.ParseRequestURI - email: an email address as parsed by Golang net/mail.ParseAddress - hostname: a valid representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034]. - ipv4: an IPv4 IP as parsed by Golang net.ParseIP - ipv6: an IPv6 IP as parsed by Golang net.ParseIP - cidr: a CIDR as parsed by Golang net.ParseCIDR - mac: a MAC address as parsed by Golang net.ParseMAC - uuid: an UUID that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$ - uuid3: an UUID3 that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$ - uuid4: an UUID4 that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$ - uuid5: an UUID5 that allows uppercase defined by the regex (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$ - isbn: an ISBN10 or ISBN13 number string like \"0321751043\" or \"978-0321751041\" - isbn10: an ISBN10 number string like \"0321751043\" - isbn13: an ISBN13 number string like \"978-0321751041\" - creditcard: a credit card number defined by the regex ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$ with any non digit characters mixed in - ssn: a U.S. social security number following the regex ^\\d{3}[- ]?\\d{2}[- ]?\\d{4}$ - hexcolor: an hexadecimal color code like \"#FFFFFF: following the regex ^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$ - rgbcolor: an RGB color code like rgb like \"rgb(255,255,2559\" - byte: base64 encoded binary data - password: any kind of string - date: a date string like \"2006-01-02\" as defined by full-date in RFC3339 - duration: a duration string like \"22 ns\" as parsed by Golang time.ParseDuration or compatible with Scala duration format - datetime: a date time string like \"2014-12-15T19:30:20.000Z\" as defined by date-time in RFC3339.", + "type": "string" + }, + "id": { + "type": "string" + }, + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrArray" + }, + "maxItems": { + "format": "int64", + "type": "integer" + }, + "maxLength": { + "format": "int64", + "type": "integer" + }, + "maxProperties": { + "format": "int64", + "type": "integer" + }, + "maximum": { + "format": "double", + "type": "number" + }, + "minItems": { + "format": "int64", + "type": "integer" + }, + "minLength": { + "format": "int64", + "type": "integer" + }, + "minProperties": { + "format": "int64", + "type": "integer" + }, + "minimum": { + "format": "double", + "type": "number" + }, + "multipleOf": { + "format": "double", + "type": "number" + }, + "not": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "nullable": { + "type": "boolean" + }, + "oneOf": { + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "type": "array" + }, + "pattern": { + "type": "string" + }, + "patternProperties": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "type": "object" + }, + "properties": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" + }, + "type": "object" + }, + "required": { + "items": { + "type": "string" + }, + "type": "array" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uniqueItems": { + "type": "boolean" + }, + "x-kubernetes-embedded-resource": { + "description": "x-kubernetes-embedded-resource defines that the value is an embedded Kubernetes runtime.Object, with TypeMeta and ObjectMeta. The type must be object. It is allowed to further restrict the embedded object. kind, apiVersion and metadata are validated automatically. x-kubernetes-preserve-unknown-fields is allowed to be true, but does not have to be if the object is fully specified (up to kind, apiVersion, metadata).", + "type": "boolean" + }, + "x-kubernetes-int-or-string": { + "description": "x-kubernetes-int-or-string specifies that this value is either an integer or a string. If this is true, an empty type is allowed and type as child of anyOf is permitted if following one of the following patterns:\n\n1) anyOf:\n - type: integer\n - type: string\n2) allOf:\n - anyOf:\n - type: integer\n - type: string\n - ... zero or more", + "type": "boolean" + }, + "x-kubernetes-list-map-keys": { + "description": "x-kubernetes-list-map-keys annotates an array with the x-kubernetes-list-type `map` by specifying the keys used as the index of the map.\n\nThis tag MUST only be used on lists that have the \"x-kubernetes-list-type\" extension set to \"map\". Also, the values specified for this attribute must be a scalar typed field of the child structure (no nesting is supported).\n\nThe properties specified must either be required or have a default value, to ensure those properties are present for all list items.", + "items": { + "type": "string" + }, + "type": "array" + }, + "x-kubernetes-list-type": { + "description": "x-kubernetes-list-type annotates an array to further describe its topology. This extension must only be used on lists and may have 3 possible values:\n\n1) `atomic`: the list is treated as a single entity, like a scalar.\n Atomic lists will be entirely replaced when updated. This extension\n may be used on any type of list (struct, scalar, ...).\n2) `set`:\n Sets are lists that must not have multiple items with the same value. Each\n value must be a scalar, an object with x-kubernetes-map-type `atomic` or an\n array with x-kubernetes-list-type `atomic`.\n3) `map`:\n These lists are like maps in that their elements have a non-index key\n used to identify them. Order is preserved upon merge. The map tag\n must only be used on a list with elements of type object.\nDefaults to atomic for arrays.", + "type": "string" + }, + "x-kubernetes-map-type": { + "description": "x-kubernetes-map-type annotates an object to further describe its topology. This extension must only be used when type is object and may have 2 possible values:\n\n1) `granular`:\n These maps are actual maps (key-value pairs) and each fields are independent\n from each other (they can each be manipulated by separate actors). This is\n the default behaviour for all maps.\n2) `atomic`: the list is treated as a single entity, like a scalar.\n Atomic maps will be entirely replaced when updated.", + "type": "string" + }, + "x-kubernetes-preserve-unknown-fields": { + "description": "x-kubernetes-preserve-unknown-fields stops the API server decoding step from pruning fields which are not specified in the validation schema. This affects fields recursively, but switches back to normal pruning behaviour if nested properties or additionalProperties are specified in the schema. This can either be true or undefined. False is forbidden.", + "type": "boolean" + }, + "x-kubernetes-validations": { + "description": "x-kubernetes-validations describes a list of validation rules written in the CEL expression language. This field is an alpha-level. Using this field requires the feature gate `CustomResourceValidationExpressions` to be enabled.", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.ValidationRule" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "rule" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "rule", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrArray": { + "description": "JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps or an array of JSONSchemaProps. Mainly here for serialization purposes." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool": { + "description": "JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value. Defaults to true for the boolean property." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrStringArray": { + "description": "JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array." + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "name is the name of the service. Required", + "type": "string" + }, + "namespace": { + "description": "namespace is the namespace of the service. Required", + "type": "string" + }, + "path": { + "description": "path is an optional URL path at which the webhook will be contacted.", + "type": "string" + }, + "port": { + "description": "port is an optional service port at which the webhook will be contacted. `port` should be a valid port number (1-65535, inclusive). Defaults to 443 for backward compatibility.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "namespace", + "name" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.ValidationRule": { + "description": "ValidationRule describes a validation rule written in the CEL expression language.", + "properties": { + "fieldPath": { + "description": "fieldPath represents the field path returned when the validation fails. It must be a relative JSON path (i.e. with array notation) scoped to the location of this x-kubernetes-validations extension in the schema and refer to an existing field. e.g. when validation checks if a specific attribute `foo` under a map `testMap`, the fieldPath could be set to `.testMap.foo` If the validation checks two lists must have unique attributes, the fieldPath could be set to either of the list: e.g. `.testList` It does not support list numeric index. It supports child operation to refer to an existing field currently. Refer to [JSONPath support in Kubernetes](https://kubernetes.io/docs/reference/kubectl/jsonpath/) for more info. Numeric index of array is not supported. For field name which contains special characters, use `['specialName']` to refer the field name. e.g. for attribute `foo.34$` appears in a list `testList`, the fieldPath could be set to `.testList['foo.34$']`", + "type": "string" + }, + "message": { + "description": "Message represents the message displayed when validation fails. The message is required if the Rule contains line breaks. The message must not contain line breaks. If unset, the message is \"failed rule: {Rule}\". e.g. \"must be a URL with the host matching spec.host\"", + "type": "string" + }, + "messageExpression": { + "description": "MessageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. Since messageExpression is used as a failure message, it must evaluate to a string. If both message and messageExpression are present on a rule, then messageExpression will be used if validation fails. If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. messageExpression has access to all the same variables as the rule; the only difference is the return type. Example: \"x must be less than max (\"+string(self.max)+\")\"", + "type": "string" + }, + "reason": { + "description": "reason provides a machine-readable validation failure reason that is returned to the caller when a request fails this validation rule. The HTTP status code returned to the caller will match the reason of the reason of the first failed validation rule. The currently supported reasons are: \"FieldValueInvalid\", \"FieldValueForbidden\", \"FieldValueRequired\", \"FieldValueDuplicate\". If not set, default to use \"FieldValueInvalid\". All future added reasons must be accepted by clients when reading this value and unknown reasons should be treated as FieldValueInvalid.", + "type": "string" + }, + "rule": { + "description": "Rule represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec The Rule is scoped to the location of the x-kubernetes-validations extension in the schema. The `self` variable in the CEL expression is bound to the scoped value. Example: - Rule scoped to the root of a resource with a status subresource: {\"rule\": \"self.status.actual <= self.spec.maxDesired\"}\n\nIf the Rule is scoped to an object with properties, the accessible properties of the object are field selectable via `self.field` and field presence can be checked via `has(self.field)`. Null valued fields are treated as absent fields in CEL expressions. If the Rule is scoped to an object with additionalProperties (i.e. a map) the value of the map are accessible via `self[mapKey]`, map containment can be checked via `mapKey in self` and all entries of the map are accessible via CEL macros and functions such as `self.all(...)`. If the Rule is scoped to an array, the elements of the array are accessible via `self[i]` and also by macros and functions. If the Rule is scoped to a scalar, `self` is bound to the scalar value. Examples: - Rule scoped to a map of objects: {\"rule\": \"self.components['Widget'].priority < 10\"} - Rule scoped to a list of integers: {\"rule\": \"self.values.all(value, value >= 0 && value < 100)\"} - Rule scoped to a string value: {\"rule\": \"self.startsWith('kube')\"}\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object and from any x-kubernetes-embedded-resource annotated objects. No other metadata properties are accessible.\n\nUnknown data preserved in custom resources via x-kubernetes-preserve-unknown-fields is not accessible in CEL expressions. This includes: - Unknown field values that are preserved by object schemas with x-kubernetes-preserve-unknown-fields. - Object properties where the property schema is of an \"unknown type\". An \"unknown type\" is recursively defined as:\n - A schema with no type and x-kubernetes-preserve-unknown-fields set to true\n - An array where the items schema is of an \"unknown type\"\n - An object where the additionalProperties schema is of an \"unknown type\"\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Rule accessing a property named \"namespace\": {\"rule\": \"self.__namespace__ > 0\"}\n - Rule accessing a property named \"x-prop\": {\"rule\": \"self.x__dash__prop > 0\"}\n - Rule accessing a property named \"redact__d\": {\"rule\": \"self.redact__underscores__d > 0\"}\n\nEquality on arrays with x-kubernetes-list-type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.", + "type": "string" + } + }, + "required": [ + "rule" + ], + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.WebhookClientConfig": { + "description": "WebhookClientConfig contains the information to make a TLS connection with the webhook.", + "properties": { + "caBundle": { + "description": "caBundle is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string" + }, + "service": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.ServiceReference", + "description": "service is a reference to the service for this webhook. Either service or url must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`." + }, + "url": { + "description": "url gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.WebhookConversion": { + "description": "WebhookConversion describes how to call a conversion webhook", + "properties": { + "clientConfig": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.WebhookClientConfig", + "description": "clientConfig is the instructions for how to call the webhook if strategy is `Webhook`." + }, + "conversionReviewVersions": { + "description": "conversionReviewVersions is an ordered list of preferred `ConversionReview` versions the Webhook expects. The API server will use the first version in the list which it supports. If none of the versions specified in this list are supported by API server, conversion will fail for the custom resource. If a persisted Webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "conversionReviewVersions" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.api.resource.Quantity": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup": { + "description": "APIGroup contains the name, the supported versions, and the preferred version of a group.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIGroup" + ] + }, + "name": { + "description": "name is the name of the group.", + "type": "string" + }, + "preferredVersion": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery", + "description": "preferredVersion is the version preferred by the API server, which probably is the storage version." + }, + "serverAddressByClientCIDRs": { + "description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR" + }, + "type": "array" + }, + "versions": { + "description": "versions are the versions supported in this group.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery" + }, + "type": "array" + } + }, + "required": [ + "name", + "versions" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIGroup", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIGroupList": { + "description": "APIGroupList is a list of APIGroup, to allow clients to discover the API at /apis.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "groups": { + "description": "groups is a list of APIGroup.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIGroupList" + ] + } + }, + "required": [ + "groups" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIGroupList", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "items": { + "type": "string" + }, + "type": "array" + }, + "group": { + "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + "type": "string" + }, + "kind": { + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string" + }, + "name": { + "description": "name is the plural name of the resource.", + "type": "string" + }, + "namespaced": { + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean" + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "items": { + "type": "string" + }, + "type": "array" + }, + "singularName": { + "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + "type": "string" + }, + "storageVersionHash": { + "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "items": { + "type": "string" + }, + "type": "array" + }, + "version": { + "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + "type": "string" + } + }, + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "groupVersion": { + "description": "groupVersion is the group and version this APIResourceList is for.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIResourceList" + ] + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + }, + "type": "array" + } + }, + "required": [ + "groupVersion", + "resources" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIResourceList", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIVersions": { + "description": "APIVersions lists the versions that are available, to allow clients to discover the API at /api, which is the root path of the legacy v1 API.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIVersions" + ] + }, + "serverAddressByClientCIDRs": { + "description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR" + }, + "type": "array" + }, + "versions": { + "description": "versions are the api versions that are available.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "versions", + "serverAddressByClientCIDRs" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIVersions", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Condition": { + "description": "Condition contains details for one aspect of the current state of this API Resource.", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable." + }, + "message": { + "description": "message is a human readable message indicating details about the transition. This may be an empty string.", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", + "format": "int64", + "type": "integer" + }, + "reason": { + "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.", + "type": "string" + }, + "status": { + "description": "status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "type of condition in CamelCase or in foo.example.com/CamelCase.", + "type": "string" + } + }, + "required": [ + "type", + "status", + "lastTransitionTime", + "reason", + "message" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { + "description": "DeleteOptions may be provided when deleting an API object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "dryRun": { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "items": { + "type": "string" + }, + "type": "array" + }, + "gracePeriodSeconds": { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "format": "int64", + "type": "integer" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "DeleteOptions" + ] + }, + "orphanDependents": { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "type": "boolean" + }, + "preconditions": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions", + "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned." + }, + "propagationPolicy": { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "discovery.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "discovery.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta3" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "internal.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha2" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { + "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery": { + "description": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.", + "properties": { + "groupVersion": { + "description": "groupVersion specifies the API group and version in the form \"group/version\"", + "type": "string" + }, + "version": { + "description": "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion.", + "type": "string" + } + }, + "required": [ + "groupVersion", + "version" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector": { + "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement" + }, + "type": "array" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "properties": { + "continue": { + "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + "type": "string" + }, + "remainingItemCount": { + "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + "format": "int64", + "type": "integer" + }, + "resourceVersion": { + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1", + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type." + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "subresource": { + "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", + "type": "string" + }, + "time": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over." + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime": { + "description": "MicroTime is version of Time with microsecond level precision.", + "format": "date-time", + "type": "string" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "properties": { + "annotations": { + "additionalProperties": { + "type": "string" + }, + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", + "type": "object" + }, + "creationTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "deletionGracePeriodSeconds": { + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + "format": "int64", + "type": "integer" + }, + "deletionTimestamp": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "finalizers": { + "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "format": "int64", + "type": "integer" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", + "type": "object" + }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + }, + "type": "array" + }, + "name": { + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + }, + "uid": { + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string" + }, + "blockOwnerDeletion": { + "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "uid": { + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { + "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + "properties": { + "resourceVersion": { + "description": "Specifies the target ResourceVersion", + "type": "string" + }, + "uid": { + "description": "Specifies the target UID.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR": { + "description": "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.", + "properties": { + "clientCIDR": { + "description": "The CIDR with which clients can match their IP to figure out the server address that they should use.", + "type": "string" + }, + "serverAddress": { + "description": "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port.", + "type": "string" + } + }, + "required": [ + "clientCIDR", + "serverAddress" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { + "description": "Status is a return value for calls that don't return other objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "Suggested HTTP return code for this status, 0 if not set.", + "format": "int32", + "type": "integer" + }, + "details": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails", + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "Status" + ] + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + }, + "reason": { + "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + "type": "string" + }, + "status": { + "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Status", + "version": "v1" + }, + { + "group": "resource.k8s.io", + "kind": "Status", + "version": "v1alpha2" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "properties": { + "field": { + "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + "type": "string" + }, + "message": { + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + "type": "string" + }, + "reason": { + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "properties": { + "causes": { + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" + }, + "type": "array" + }, + "group": { + "description": "The group attribute of the resource associated with the status StatusReason.", + "type": "string" + }, + "kind": { + "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "type": "string" + }, + "retryAfterSeconds": { + "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + "format": "int32", + "type": "integer" + }, + "uid": { + "description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "format": "date-time", + "type": "string" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent": { + "description": "Event represents a single event to a watched resource.", + "properties": { + "object": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension", + "description": "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context." + }, + "type": { + "type": "string" + } + }, + "required": [ + "type", + "object" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "discovery.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "discovery.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta3" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "internal.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha2" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.runtime.RawExtension": { + "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + "type": "object" + }, + "io.k8s.apimachinery.pkg.util.intstr.IntOrString": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "io.k8s.apimachinery.pkg.version.Info": { + "description": "Info contains versioning information. how we'll want to distribute that information.", + "properties": { + "buildDate": { + "type": "string" + }, + "compiler": { + "type": "string" + }, + "gitCommit": { + "type": "string" + }, + "gitTreeState": { + "type": "string" + }, + "gitVersion": { + "type": "string" + }, + "goVersion": { + "type": "string" + }, + "major": { + "type": "string" + }, + "minor": { + "type": "string" + }, + "platform": { + "type": "string" + } + }, + "required": [ + "major", + "minor", + "gitVersion", + "gitCommit", + "gitTreeState", + "buildDate", + "goVersion", + "compiler", + "platform" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService": { + "description": "APIService represents a server for a particular GroupVersion. Name must be \"version.group\".", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIService" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec", + "description": "Spec contains information for locating and communicating with a server" + }, + "status": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus", + "description": "Status contains derived information about an API server" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiregistration.k8s.io", + "kind": "APIService", + "version": "v1" + } + ] + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition": { + "description": "APIServiceCondition describes the state of an APIService at a particular point", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", + "description": "Last time the condition transitioned from one status to another." + }, + "message": { + "description": "Human-readable message indicating details about last transition.", + "type": "string" + }, + "reason": { + "description": "Unique, one-word, CamelCase reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status is the status of the condition. Can be True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type is the type of the condition.", + "type": "string" + } + }, + "required": [ + "type", + "status" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceList": { + "description": "APIServiceList is a list of APIService objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of APIService", + "items": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "enum": [ + "APIServiceList" + ] + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "apiregistration.k8s.io", + "kind": "APIServiceList", + "version": "v1" + } + ] + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec": { + "description": "APIServiceSpec contains information for locating and communicating with a server. Only https is supported, though you are able to disable certificate verification.", + "properties": { + "caBundle": { + "description": "CABundle is a PEM encoded CA bundle which will be used to validate an API server's serving certificate. If unspecified, system trust roots on the apiserver are used.", + "format": "byte", + "type": "string", + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "Group is the API group name this server hosts", + "type": "string" + }, + "groupPriorityMinimum": { + "description": "GroupPriorityMininum is the priority this group should have at least. Higher priority means that the group is preferred by clients over lower priority ones. Note that other versions of this group might specify even higher GroupPriorityMininum values such that the whole group gets a higher priority. The primary sort is based on GroupPriorityMinimum, ordered highest number to lowest (20 before 10). The secondary sort is based on the alphabetical comparison of the name of the object. (v1.bar before v1.foo) We'd recommend something like: *.k8s.io (except extensions) at 18000 and PaaSes (OpenShift, Deis) are recommended to be in the 2000s", + "format": "int32", + "type": "integer" + }, + "insecureSkipTLSVerify": { + "description": "InsecureSkipTLSVerify disables TLS certificate verification when communicating with this server. This is strongly discouraged. You should use the CABundle instead.", + "type": "boolean" + }, + "service": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference", + "description": "Service is a reference to the service for this API server. It must communicate on port 443. If the Service is nil, that means the handling for the API groupversion is handled locally on this server. The call will simply delegate to the normal handler chain to be fulfilled." + }, + "version": { + "description": "Version is the API version this server hosts. For example, \"v1\"", + "type": "string" + }, + "versionPriority": { + "description": "VersionPriority controls the ordering of this API version inside of its group. Must be greater than zero. The primary sort is based on VersionPriority, ordered highest to lowest (20 before 10). Since it's inside of a group, the number can be small, probably in the 10s. In case of equal version priorities, the version string will be used to compute the order inside a group. If the version string is \"kube-like\", it will sort above non \"kube-like\" version strings, which are ordered lexicographically. \"Kube-like\" versions start with a \"v\", then are followed by a number (the major version), then optionally the string \"alpha\" or \"beta\" and another number (the minor version). These are sorted first by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing major version, then minor version. An example sorted list of versions: v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "groupPriorityMinimum", + "versionPriority" + ], + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus": { + "description": "APIServiceStatus contains derived information about an API server", + "properties": { + "conditions": { + "description": "Current service state of apiService.", + "items": { + "$ref": "#/definitions/io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference": { + "description": "ServiceReference holds a reference to Service.legacy.k8s.io", + "properties": { + "name": { + "description": "Name is the name of the service", + "type": "string" + }, + "namespace": { + "description": "Namespace is the namespace of the service", + "type": "string" + }, + "port": { + "description": "If specified, the port on the service that hosting webhook. Default to 443 for backward compatibility. `port` should be a valid port number (1-65535, inclusive).", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + } + } +} \ No newline at end of file diff --git a/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java b/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java index 10e97782609..0710d318f33 100644 --- a/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java +++ b/json-schema-generator/src/test/java/oracle/kubernetes/json/SchemaGeneratorTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.json; @@ -27,7 +27,7 @@ class SchemaGeneratorTest { private static final String K8S_SCHEMA_URL = - "https://github.com/garethr/kubernetes-json-schema/blob/master/v1.9.0/_definitions.json"; + "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.9.0/_definitions.json"; private static final String K8S_CACHE_FILE = "caches/kubernetes-1.9.0.json"; private final SchemaGenerator generator = new SchemaGenerator(); diff --git a/json-schema-generator/src/test/java/oracle/kubernetes/json/YamlDocGeneratorTest.java b/json-schema-generator/src/test/java/oracle/kubernetes/json/YamlDocGeneratorTest.java index eedcc99e682..643dd4532d1 100644 --- a/json-schema-generator/src/test/java/oracle/kubernetes/json/YamlDocGeneratorTest.java +++ b/json-schema-generator/src/test/java/oracle/kubernetes/json/YamlDocGeneratorTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. +// Copyright (c) 2019, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.json; @@ -20,7 +20,7 @@ import static org.hamcrest.junit.MatcherAssert.assertThat; class YamlDocGeneratorTest { - private static final String K8S_VERSION = "1.13.5"; + private static final String K8S_VERSION = "1.28.2"; private final SchemaGenerator schemaGenerator = new SchemaGenerator(); @SuppressWarnings("unused") @Description("An annotated field") diff --git a/kubernetes/crd/cluster-crd.yaml b/kubernetes/crd/cluster-crd.yaml index 8561ee8a44c..96e1c5b11b5 100644 --- a/kubernetes/crd/cluster-crd.yaml +++ b/kubernetes/crd/cluster-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 7146de067298f75bf803e2c8acf9f88980d1d5770e375122bcbb1fe4e6f9a64b + weblogic.sha256: 25a60de9429ea0f9bad45b3083d18c9ff56c86c62a123cdff67af4bacf82eec5 name: clusters.weblogic.oracle spec: group: weblogic.oracle @@ -264,34 +264,77 @@ spec: type: array readinessProbe: description: Settings for the readiness probe associated with - a WebLogic Server instance. + a WebLogic Server instance. If not specified, the operator will + create an HTTP probe accessing the /weblogic/ready path. If + an HTTP probe is specified then the operator will fill in `path`, + `port`, and `scheme`, if they are missing. The operator will + also fill in any missing tuning-related fields if they are unspecified. + Tuning-related fields will be inherited from the domain and + cluster scopes unless a more specific scope defines a different + action, such as a different HTTP path to access. properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed before - giving up. Giving up in case of liveness probe means restarting - the container. In case of readiness probe, the Pod will - be marked Unready. Defaults to 1. - minimum: 1 type: integer periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object timeoutSeconds: - description: The number of seconds with no response that indicates - a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs to pass - for the probe to be considered successful after having failed. - Defaults to 1. Must be 1 for liveness Probe. - minimum: 1 type: integer initialDelaySeconds: - description: The number of seconds before the first check - is performed. type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object type: object containerSecurityContext: description: 'Container-level security attributes. Will override @@ -396,34 +439,75 @@ spec: type: integer livenessProbe: description: Settings for the liveness probe associated with a - WebLogic Server instance. + WebLogic Server instance. If not specified, the operator will + create a probe that executes a script provided by the operator. + The operator will also fill in any missing tuning-related fields, + if they are unspecified. Tuning-related fields will be inherited + from the domain and cluster scopes unless a more specific scope + defines a different action, such as a different script to execute. properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed before - giving up. Giving up in case of liveness probe means restarting - the container. In case of readiness probe, the Pod will - be marked Unready. Defaults to 1. - minimum: 1 type: integer periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object timeoutSeconds: - description: The number of seconds with no response that indicates - a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs to pass - for the probe to be considered successful after having failed. - Defaults to 1. Must be 1 for liveness Probe. - minimum: 1 type: integer initialDelaySeconds: - description: The number of seconds before the first check - is performed. type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object type: object topologySpreadConstraints: description: TopologySpreadConstraints describes how a group of diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 3de269e2e40..7f9557e629b 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 8eb63425366f1b880048f26915d7a1943d788a13ed7a483aeb8081848d4ea971 + weblogic.sha256: ee0c6f79288b000cd61aa9438ab7f24f63b6334072e3520f25ee454f8bff6175 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -1360,35 +1360,78 @@ spec: type: array readinessProbe: description: Settings for the readiness probe associated with - a WebLogic Server instance. + a WebLogic Server instance. If not specified, the operator + will create an HTTP probe accessing the /weblogic/ready + path. If an HTTP probe is specified then the operator will + fill in `path`, `port`, and `scheme`, if they are missing. + The operator will also fill in any missing tuning-related + fields if they are unspecified. Tuning-related fields will + be inherited from the domain and cluster scopes unless a + more specific scope defines a different action, such as + a different HTTP path to access. properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed before - giving up. Giving up in case of liveness probe means - restarting the container. In case of readiness probe, - the Pod will be marked Unready. Defaults to 1. - minimum: 1 type: integer periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object timeoutSeconds: - description: The number of seconds with no response that - indicates a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs to - pass for the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for liveness - Probe. - minimum: 1 type: integer initialDelaySeconds: - description: The number of seconds before the first check - is performed. type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object type: object containerSecurityContext: description: 'Container-level security attributes. Will override @@ -1494,35 +1537,76 @@ spec: type: integer livenessProbe: description: Settings for the liveness probe associated with - a WebLogic Server instance. + a WebLogic Server instance. If not specified, the operator + will create a probe that executes a script provided by the + operator. The operator will also fill in any missing tuning-related + fields, if they are unspecified. Tuning-related fields will + be inherited from the domain and cluster scopes unless a + more specific scope defines a different action, such as + a different script to execute. properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed before - giving up. Giving up in case of liveness probe means - restarting the container. In case of readiness probe, - the Pod will be marked Unready. Defaults to 1. - minimum: 1 type: integer periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object timeoutSeconds: - description: The number of seconds with no response that - indicates a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs to - pass for the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for liveness - Probe. - minimum: 1 type: integer initialDelaySeconds: - description: The number of seconds before the first check - is performed. type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object type: object topologySpreadConstraints: description: TopologySpreadConstraints describes how a group @@ -4455,34 +4539,77 @@ spec: type: array readinessProbe: description: Settings for the readiness probe associated with - a WebLogic Server instance. + a WebLogic Server instance. If not specified, the operator will + create an HTTP probe accessing the /weblogic/ready path. If + an HTTP probe is specified then the operator will fill in `path`, + `port`, and `scheme`, if they are missing. The operator will + also fill in any missing tuning-related fields if they are unspecified. + Tuning-related fields will be inherited from the domain and + cluster scopes unless a more specific scope defines a different + action, such as a different HTTP path to access. properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed before - giving up. Giving up in case of liveness probe means restarting - the container. In case of readiness probe, the Pod will - be marked Unready. Defaults to 1. - minimum: 1 type: integer periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object timeoutSeconds: - description: The number of seconds with no response that indicates - a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs to pass - for the probe to be considered successful after having failed. - Defaults to 1. Must be 1 for liveness Probe. - minimum: 1 type: integer initialDelaySeconds: - description: The number of seconds before the first check - is performed. type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object type: object containerSecurityContext: description: 'Container-level security attributes. Will override @@ -4587,34 +4714,75 @@ spec: type: integer livenessProbe: description: Settings for the liveness probe associated with a - WebLogic Server instance. + WebLogic Server instance. If not specified, the operator will + create a probe that executes a script provided by the operator. + The operator will also fill in any missing tuning-related fields, + if they are unspecified. Tuning-related fields will be inherited + from the domain and cluster scopes unless a more specific scope + defines a different action, such as a different script to execute. properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed before - giving up. Giving up in case of liveness probe means restarting - the container. In case of readiness probe, the Pod will - be marked Unready. Defaults to 1. - minimum: 1 type: integer periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object timeoutSeconds: - description: The number of seconds with no response that indicates - a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs to pass - for the probe to be considered successful after having failed. - Defaults to 1. Must be 1 for liveness Probe. - minimum: 1 type: integer initialDelaySeconds: - description: The number of seconds before the first check - is performed. type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object type: object topologySpreadConstraints: description: TopologySpreadConstraints describes how a group of @@ -7195,37 +7363,79 @@ spec: type: string readinessProbe: description: Settings for the readiness probe associated - with a WebLogic Server instance. + with a WebLogic Server instance. If not specified, the + operator will create an HTTP probe accessing the /weblogic/ready + path. If an HTTP probe is specified then the operator + will fill in `path`, `port`, and `scheme`, if they are + missing. The operator will also fill in any missing tuning-related + fields if they are unspecified. Tuning-related fields + will be inherited from the domain and cluster scopes unless + a more specific scope defines a different action, such + as a different HTTP path to access. type: object properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed - before giving up. Giving up in case of liveness probe - means restarting the container. In case of readiness - probe, the Pod will be marked Unready. Defaults to - 1. type: integer - minimum: 1 periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port timeoutSeconds: - description: The number of seconds with no response - that indicates a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs - to pass for the probe to be considered successful - after having failed. Defaults to 1. Must be 1 for - liveness Probe. type: integer - minimum: 1 initialDelaySeconds: - description: The number of seconds before the first - check is performed. type: integer + exec: + type: object + properties: + command: + type: array + items: + type: string + grpc: + type: object + properties: + port: + type: integer + service: + type: string + required: + - port + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port containerSecurityContext: description: 'Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl @@ -7331,37 +7541,77 @@ spec: type: integer livenessProbe: description: Settings for the liveness probe associated - with a WebLogic Server instance. + with a WebLogic Server instance. If not specified, the + operator will create a probe that executes a script provided + by the operator. The operator will also fill in any missing + tuning-related fields, if they are unspecified. Tuning-related + fields will be inherited from the domain and cluster scopes + unless a more specific scope defines a different action, + such as a different script to execute. type: object properties: + terminationGracePeriodSeconds: + type: integer failureThreshold: - default: 1 - description: Number of times the check is performed - before giving up. Giving up in case of liveness probe - means restarting the container. In case of readiness - probe, the Pod will be marked Unready. Defaults to - 1. type: integer - minimum: 1 periodSeconds: - description: The number of seconds between checks. type: integer + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port timeoutSeconds: - description: The number of seconds with no response - that indicates a failure. type: integer successThreshold: - default: 1 - description: Minimum number of times the check needs - to pass for the probe to be considered successful - after having failed. Defaults to 1. Must be 1 for - liveness Probe. type: integer - minimum: 1 initialDelaySeconds: - description: The number of seconds before the first - check is performed. type: integer + exec: + type: object + properties: + command: + type: array + items: + type: string + grpc: + type: object + properties: + port: + type: integer + service: + type: string + required: + - port + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port topologySpreadConstraints: description: TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler diff --git a/operator/pom.xml b/operator/pom.xml index 1ba047cc940..ecdd2ce6eb9 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -1,4 +1,4 @@ - @@ -170,7 +170,7 @@ oracle.kubernetes.weblogic.domain.model.DomainResource oracle/kubernetes/weblogic/domain/model/Domain.json - 1.13.5 + 1.28.2 true @@ -183,7 +183,7 @@ oracle.kubernetes.weblogic.domain.model.ClusterResource oracle/kubernetes/weblogic/domain/model/Cluster.json - 1.13.5 + 1.28.2 true diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index 05b25dedaec..b33c61de4c1 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -41,6 +41,7 @@ import io.kubernetes.client.openapi.models.V1PodSpec; import io.kubernetes.client.openapi.models.V1PodSpecBuilder; import io.kubernetes.client.openapi.models.V1Probe; +import io.kubernetes.client.openapi.models.V1ProbeBuilder; import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1SecretVolumeSource; import io.kubernetes.client.openapi.models.V1Volume; @@ -907,25 +908,44 @@ private V1ExecAction execAction(String... commandItems) { } private V1Probe createReadinessProbe(PodTuning tuning) { - V1Probe readinessProbe = new V1Probe(); - readinessProbe - .initialDelaySeconds(getReadinessProbeInitialDelaySeconds(tuning)) - .timeoutSeconds(getReadinessProbeTimeoutSeconds(tuning)) - .periodSeconds(getReadinessProbePeriodSeconds(tuning)) - .failureThreshold(getReadinessProbeFailureThreshold(tuning)); + V1Probe readinessProbe = getReadinessProbe(); - // Add the success threshold only if the value is non-default to avoid pod roll. - if (getReadinessProbeSuccessThreshold(tuning) != DEFAULT_SUCCESS_THRESHOLD) { - readinessProbe.successThreshold(getReadinessProbeSuccessThreshold(tuning)); + if (readinessProbe.getInitialDelaySeconds() == null) { + readinessProbe.setInitialDelaySeconds(tuning.getReadinessProbeInitialDelaySeconds()); + } + if (readinessProbe.getTimeoutSeconds() == null) { + readinessProbe.setTimeoutSeconds(tuning.getReadinessProbeTimeoutSeconds()); + } + if (readinessProbe.getPeriodSeconds() == null) { + readinessProbe.setPeriodSeconds(tuning.getReadinessProbePeriodSeconds()); + } + if (readinessProbe.getFailureThreshold() == null) { + readinessProbe.setFailureThreshold(tuning.getReadinessProbeFailureThreshold()); + } + if (readinessProbe.getSuccessThreshold() == null + && tuning.getReadinessProbeSuccessThreshold() != DEFAULT_SUCCESS_THRESHOLD) { + readinessProbe.setSuccessThreshold(tuning.getReadinessProbeSuccessThreshold()); } try { - readinessProbe = - readinessProbe.httpGet( - httpGetAction( - READINESS_PATH, - getLocalAdminProtocolChannelPort(), - isLocalAdminProtocolChannelSecure())); + V1HTTPGetAction httpGetAction = readinessProbe.getHttpGet(); + if (httpGetAction != null) { + if (httpGetAction.getPath() == null) { + httpGetAction.setPath(READINESS_PATH); + } + if (httpGetAction.getPort() == null) { + httpGetAction.setPort(new IntOrString(getLocalAdminProtocolChannelPort())); + } + if (httpGetAction.getScheme() == null && isLocalAdminProtocolChannelSecure()) { + httpGetAction.setScheme("HTTPS"); + } + } else if (readinessProbe.getExec() == null + && readinessProbe.getTcpSocket() == null && readinessProbe.getGrpc() == null) { + readinessProbe.setHttpGet(new V1HTTPGetAction() + .path(READINESS_PATH) + .port(new IntOrString(getLocalAdminProtocolChannelPort())) + .scheme(isLocalAdminProtocolChannelSecure() ? "HTTPS" : null)); + } } catch (Exception e) { // do nothing } @@ -933,77 +953,54 @@ private V1Probe createReadinessProbe(PodTuning tuning) { } @SuppressWarnings("SameParameterValue") - private V1HTTPGetAction httpGetAction(String path, int port, boolean useHttps) { - V1HTTPGetAction getAction = new V1HTTPGetAction(); - getAction.path(path).port(new IntOrString(port)); - if (useHttps) { - getAction.scheme("HTTPS"); - } - return getAction; - } - - private int getReadinessProbePeriodSeconds(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getReadinessProbe().getPeriodSeconds()) - .orElse(tuning.getReadinessProbePeriodSeconds()); - } - - private int getReadinessProbeTimeoutSeconds(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getReadinessProbe().getTimeoutSeconds()) - .orElse(tuning.getReadinessProbeTimeoutSeconds()); - } - - private int getReadinessProbeInitialDelaySeconds(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getReadinessProbe().getInitialDelaySeconds()) - .orElse(tuning.getReadinessProbeInitialDelaySeconds()); - } - - private int getReadinessProbeSuccessThreshold(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getReadinessProbe().getSuccessThreshold()) - .orElse(tuning.getReadinessProbeSuccessThreshold()); - } - - private int getReadinessProbeFailureThreshold(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getReadinessProbe().getFailureThreshold()) - .orElse(tuning.getReadinessProbeFailureThreshold()); + private V1Probe getReadinessProbe() { + return Optional.ofNullable(getServerSpec().getReadinessProbe()) + .map(V1ProbeBuilder::new).map(V1ProbeBuilder::build).orElse(new V1Probe()); } private V1Probe createLivenessProbe(PodTuning tuning) { - V1Probe livenessProbe = new V1Probe() - .initialDelaySeconds(getLivenessProbeInitialDelaySeconds(tuning)) - .timeoutSeconds(getLivenessProbeTimeoutSeconds(tuning)) - .periodSeconds(getLivenessProbePeriodSeconds(tuning)) - .failureThreshold(getLivenessProbeFailureThreshold(tuning)); + V1Probe livenessProbe = getLivenessProbe(); - // Add the success threshold only if the value is non-default to avoid pod roll. - if (getLivenessProbeSuccessThreshold(tuning) != DEFAULT_SUCCESS_THRESHOLD) { - livenessProbe.successThreshold(getLivenessProbeSuccessThreshold(tuning)); + if (livenessProbe.getInitialDelaySeconds() == null) { + livenessProbe.setInitialDelaySeconds(tuning.getLivenessProbeInitialDelaySeconds()); + } + if (livenessProbe.getTimeoutSeconds() == null) { + livenessProbe.setTimeoutSeconds(tuning.getLivenessProbeTimeoutSeconds()); + } + if (livenessProbe.getPeriodSeconds() == null) { + livenessProbe.setPeriodSeconds(tuning.getLivenessProbePeriodSeconds()); + } + if (livenessProbe.getFailureThreshold() == null) { + livenessProbe.setFailureThreshold(tuning.getLivenessProbeFailureThreshold()); + } + if (livenessProbe.getSuccessThreshold() == null + && tuning.getLivenessProbeSuccessThreshold() != DEFAULT_SUCCESS_THRESHOLD) { + livenessProbe.setSuccessThreshold(tuning.getLivenessProbeSuccessThreshold()); } - return livenessProbe.exec(execAction(LIVENESS_PROBE)); - } - - private int getLivenessProbeInitialDelaySeconds(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getLivenessProbe().getInitialDelaySeconds()) - .orElse(tuning.getLivenessProbeInitialDelaySeconds()); - } - - private int getLivenessProbeTimeoutSeconds(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getLivenessProbe().getTimeoutSeconds()) - .orElse(tuning.getLivenessProbeTimeoutSeconds()); - } - private int getLivenessProbePeriodSeconds(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getLivenessProbe().getPeriodSeconds()) - .orElse(tuning.getLivenessProbePeriodSeconds()); - } + try { + V1HTTPGetAction httpGetAction = livenessProbe.getHttpGet(); + if (httpGetAction != null) { + if (httpGetAction.getPort() == null) { + httpGetAction.setPort(new IntOrString(getLocalAdminProtocolChannelPort())); + } + if (httpGetAction.getScheme() == null && isLocalAdminProtocolChannelSecure()) { + httpGetAction.setScheme("HTTPS"); + } + } else if (livenessProbe.getExec() == null + && livenessProbe.getTcpSocket() == null && livenessProbe.getGrpc() == null) { + livenessProbe.setExec(execAction(LIVENESS_PROBE)); + } + } catch (Exception e) { + // do nothing + } - private int getLivenessProbeSuccessThreshold(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getLivenessProbe().getSuccessThreshold()) - .orElse(tuning.getLivenessProbeSuccessThreshold()); + return livenessProbe; } - private int getLivenessProbeFailureThreshold(PodTuning tuning) { - return Optional.ofNullable(getServerSpec().getLivenessProbe().getFailureThreshold()) - .orElse(tuning.getLivenessProbeFailureThreshold()); + private V1Probe getLivenessProbe() { + return Optional.ofNullable(getServerSpec().getLivenessProbe()) + .map(V1ProbeBuilder::new).map(V1ProbeBuilder::build).orElse(new V1Probe()); } private boolean mockWls() { diff --git a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java index f3e59d1f82b..6acc8c009e8 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java +++ b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.processing; @@ -15,13 +15,13 @@ import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1PodReadinessGate; import io.kubernetes.client.openapi.models.V1PodSecurityContext; +import io.kubernetes.client.openapi.models.V1Probe; import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1SecurityContext; import io.kubernetes.client.openapi.models.V1Toleration; import io.kubernetes.client.openapi.models.V1TopologySpreadConstraint; import io.kubernetes.client.openapi.models.V1Volume; import io.kubernetes.client.openapi.models.V1VolumeMount; -import oracle.kubernetes.weblogic.domain.model.ProbeTuning; import oracle.kubernetes.weblogic.domain.model.Shutdown; public interface EffectiveServerSpec { @@ -88,10 +88,10 @@ public interface EffectiveServerSpec { List getEnvFrom(); @Nonnull - ProbeTuning getLivenessProbe(); + V1Probe getLivenessProbe(); @Nonnull - ProbeTuning getReadinessProbe(); + V1Probe getReadinessProbe(); @Nonnull Shutdown getShutdown(); diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/ServerConfigurator.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/ServerConfigurator.java index e8fa60c0c1e..c16582fae2f 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/ServerConfigurator.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/ServerConfigurator.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain; @@ -33,6 +33,8 @@ public interface ServerConfigurator extends ServiceConfigurator { ServerConfigurator withReadinessProbeThresholds(Integer successThreshold, Integer failureThreshold); + ServerConfigurator withReadinessProbeHttpGetActionPath(String httpGetActionPath); + /** * Add a node label to the Servers' node selector. * diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java index 341c23782c7..cee13da3b04 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -18,6 +18,7 @@ import io.kubernetes.client.openapi.models.V1HostAlias; import io.kubernetes.client.openapi.models.V1PodReadinessGate; import io.kubernetes.client.openapi.models.V1PodSecurityContext; +import io.kubernetes.client.openapi.models.V1Probe; import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1SecurityContext; import io.kubernetes.client.openapi.models.V1Toleration; @@ -146,7 +147,7 @@ public void setLivenessProbeThresholds(Integer successThreshold, Integer failure serverPod.setLivenessProbeThresholds(successThreshold, failureThreshold); } - ProbeTuning getLivenessProbe() { + V1Probe getLivenessProbe() { return serverPod.getLivenessProbeTuning(); } @@ -158,7 +159,11 @@ void setReadinessProbeThresholds(Integer successThreshold, Integer failureThresh serverPod.setReadinessProbeThresholds(successThreshold, failureThreshold); } - ProbeTuning getReadinessProbe() { + void setReadinessProbeHttpGetActionPath(String httpGetActionPath) { + serverPod.setReadinessProbeHttpGetActionPath(httpGetActionPath); + } + + V1Probe getReadinessProbe() { return serverPod.getReadinessProbeTuning(); } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java index 2e827f31802..0de0124519c 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java @@ -1340,7 +1340,7 @@ private String getPvcStorageClass(PersistentVolumeClaim pvc) { } private void verifyLivenessProbeSuccessThresholdManagedServers() { - Optional.of(getAdminServerSpec().getLivenessProbe()) + Optional.ofNullable(getAdminServerSpec().getLivenessProbe()) .ifPresent(probe -> verifySuccessThresholdValue(probe, ADMIN_SERVER_POD_SPEC_PREFIX + ".livenessProbe.successThreshold")); getSpec().getManagedServers().forEach(managedServer -> diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java index 4af0baebd93..2de1c683491 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -15,6 +15,7 @@ import io.kubernetes.client.openapi.models.V1HostAlias; import io.kubernetes.client.openapi.models.V1PodReadinessGate; import io.kubernetes.client.openapi.models.V1PodSecurityContext; +import io.kubernetes.client.openapi.models.V1Probe; import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1SecurityContext; import io.kubernetes.client.openapi.models.V1Toleration; @@ -152,13 +153,13 @@ public boolean alwaysStart() { @Nonnull @Override - public ProbeTuning getLivenessProbe() { + public V1Probe getLivenessProbe() { return server.getLivenessProbe(); } @Nonnull @Override - public ProbeTuning getReadinessProbe() { + public V1Probe getReadinessProbe() { return server.getReadinessProbe(); } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ProbeTuning.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ProbeTuning.java deleted file mode 100644 index f2542c18231..00000000000 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ProbeTuning.java +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. -// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - -package oracle.kubernetes.weblogic.domain.model; - -import com.google.gson.annotations.SerializedName; -import oracle.kubernetes.json.Default; -import oracle.kubernetes.json.Description; -import oracle.kubernetes.json.Range; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; - -public class ProbeTuning { - @Description("The number of seconds before the first check is performed.") - private Integer initialDelaySeconds = null; - - @Description("The number of seconds between checks.") - @SerializedName("periodSeconds") - private Integer periodSeconds = null; - - @Description("The number of seconds with no response that indicates a failure.") - @SerializedName("timeoutSeconds") - private Integer timeoutSeconds = null; - - @Description("Number of times the check is performed before giving up. Giving up in " - + "case of liveness probe means restarting the container. In case of readiness probe, the Pod will be " - + "marked Unready. Defaults to 1.") - @SerializedName("failureThreshold") - @Range(minimum = 1) - @Default(intDefault = 1) - Integer failureThreshold = null; - - @Description("Minimum number of times the check needs to pass for the probe to be considered successful" - + " after having failed. Defaults to 1. Must be 1 for liveness Probe.") - @SerializedName("successThreshold") - @Range(minimum = 1) - @Default(intDefault = 1) - private Integer successThreshold = null; - - void copyValues(ProbeTuning fromProbe) { - if (initialDelaySeconds == null) { - initialDelaySeconds(fromProbe.initialDelaySeconds); - } - if (timeoutSeconds == null) { - timeoutSeconds(fromProbe.timeoutSeconds); - } - if (periodSeconds == null) { - periodSeconds(fromProbe.periodSeconds); - } - if (successThreshold == null) { - successThreshold(fromProbe.successThreshold); - } - if (failureThreshold == null) { - failureThreshold(fromProbe.failureThreshold); - } - } - - public Integer getInitialDelaySeconds() { - return initialDelaySeconds; - } - - public ProbeTuning initialDelaySeconds(Integer initialDelaySeconds) { - this.initialDelaySeconds = initialDelaySeconds; - return this; - } - - public Integer getPeriodSeconds() { - return periodSeconds; - } - - public ProbeTuning periodSeconds(Integer periodSeconds) { - this.periodSeconds = periodSeconds; - return this; - } - - public Integer getTimeoutSeconds() { - return timeoutSeconds; - } - - public ProbeTuning timeoutSeconds(Integer timeoutSeconds) { - this.timeoutSeconds = timeoutSeconds; - return this; - } - - public Integer getSuccessThreshold() { - return successThreshold; - } - - public ProbeTuning successThreshold(Integer successThreshold) { - this.successThreshold = successThreshold; - return this; - } - - public Integer getFailureThreshold() { - return failureThreshold; - } - - public ProbeTuning failureThreshold(Integer failureThreshold) { - this.failureThreshold = failureThreshold; - return this; - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("initialDelaySeconds", initialDelaySeconds) - .append("periodSeconds", periodSeconds) - .append("timeoutSeconds", timeoutSeconds) - .append("successThreshold", successThreshold) - .append("failureThreshold", failureThreshold) - .toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - ProbeTuning that = (ProbeTuning) o; - - return new EqualsBuilder() - .append(initialDelaySeconds, that.initialDelaySeconds) - .append(periodSeconds, that.periodSeconds) - .append(timeoutSeconds, that.timeoutSeconds) - .append(successThreshold, that.successThreshold) - .append(failureThreshold, that.failureThreshold) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(initialDelaySeconds) - .append(periodSeconds) - .append(timeoutSeconds) - .append(successThreshold) - .append(failureThreshold) - .toHashCode(); - } -} diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java index 993abbba9d6..13ea791fe95 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -19,11 +19,13 @@ import io.kubernetes.client.openapi.models.V1ContainerBuilder; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1HTTPGetAction; import io.kubernetes.client.openapi.models.V1HostAlias; import io.kubernetes.client.openapi.models.V1HostPathVolumeSource; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; import io.kubernetes.client.openapi.models.V1PodReadinessGate; import io.kubernetes.client.openapi.models.V1PodSecurityContext; +import io.kubernetes.client.openapi.models.V1Probe; import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1SecurityContext; import io.kubernetes.client.openapi.models.V1Toleration; @@ -84,8 +86,12 @@ class ServerPod extends KubernetesResource { * * @since 2.0 */ - @Description("Settings for the liveness probe associated with a WebLogic Server instance.") - private final ProbeTuning livenessProbe = new ProbeTuning(); + @Description("Settings for the liveness probe associated with a WebLogic Server instance." + + " If not specified, the operator will create a probe that executes a script provided by the operator." + + " The operator will also fill in any missing tuning-related fields, if they are unspecified." + + " Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope" + + " defines a different action, such as a different script to execute.") + private V1Probe livenessProbe = null; /** * Defines the settings for the readiness probe. Any that are not specified will default to the @@ -93,8 +99,14 @@ class ServerPod extends KubernetesResource { * * @since 2.0 */ - @Description("Settings for the readiness probe associated with a WebLogic Server instance.") - private final ProbeTuning readinessProbe = new ProbeTuning(); + @Description("Settings for the readiness probe associated with a WebLogic Server instance." + + " If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path." + + " If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`," + + " if they are missing. The operator will also fill in any missing tuning-related fields" + + " if they are unspecified." + + " Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope" + + " defines a different action, such as a different HTTP path to access.") + private V1Probe readinessProbe = null; /** * Defines the key-value pairs for the pod to fit on a node, the node must have each of the @@ -277,6 +289,34 @@ public void setVolumes(List volumes) { + "state before it considers the pod failed. Defaults to 5 minutes.") private Long maxPendingWaitTimeSeconds = null; + private static boolean hasAnAction(V1Probe probe) { + return probe.getExec() != null || probe.getHttpGet() != null + || probe.getGrpc() != null || probe.getTcpSocket() != null; + } + + private static void copyValues(V1Probe to, V1Probe from) { + if (from != null && !(hasAnAction(to) && hasAnAction(from))) { + if (to.getInitialDelaySeconds() == null) { + to.setInitialDelaySeconds(from.getInitialDelaySeconds()); + } + if (to.getTimeoutSeconds() == null) { + to.setTimeoutSeconds(from.getTimeoutSeconds()); + } + if (to.getPeriodSeconds() == null) { + to.setPeriodSeconds(from.getPeriodSeconds()); + } + if (to.getSuccessThreshold() == null) { + to.setSuccessThreshold(from.getSuccessThreshold()); + } + if (to.getFailureThreshold() == null) { + to.setFailureThreshold(from.getFailureThreshold()); + } + if (to.getTerminationGracePeriodSeconds() == null) { + to.setTerminationGracePeriodSeconds(from.getTerminationGracePeriodSeconds()); + } + } + } + private static void copyValues(V1ResourceRequirements to, V1ResourceRequirements from) { if (from != null) { if (from.getRequests() != null) { @@ -371,36 +411,41 @@ void setShutdown(ShutdownType shutdownType, Long timeoutSeconds, Boolean ignoreS .skipWaitingCohEndangeredState(skipWaitingCohEndangeredState); } - ProbeTuning getReadinessProbeTuning() { + V1Probe getReadinessProbeTuning() { return this.readinessProbe; } void setReadinessProbeTuning(Integer initialDelay, Integer timeout, Integer period) { - this.readinessProbe + readinessProbe = Optional.ofNullable(readinessProbe).orElse(new V1Probe()) .initialDelaySeconds(initialDelay) .timeoutSeconds(timeout) .periodSeconds(period); } void setReadinessProbeThresholds(Integer successThreshold, Integer failureThreshold) { - this.readinessProbe + readinessProbe = Optional.ofNullable(readinessProbe).orElse(new V1Probe()) .successThreshold(successThreshold) .failureThreshold(failureThreshold); } - ProbeTuning getLivenessProbeTuning() { + void setReadinessProbeHttpGetActionPath(String httpGetActionPath) { + readinessProbe = Optional.ofNullable(readinessProbe).orElse(new V1Probe()) + .httpGet(new V1HTTPGetAction().path(httpGetActionPath)); + } + + V1Probe getLivenessProbeTuning() { return this.livenessProbe; } void setLivenessProbe(Integer initialDelay, Integer timeout, Integer period) { - this.livenessProbe + livenessProbe = Optional.ofNullable(livenessProbe).orElse(new V1Probe()) .initialDelaySeconds(initialDelay) .timeoutSeconds(timeout) .periodSeconds(period); } void setLivenessProbeThresholds(Integer successThreshold, Integer failureThreshold) { - this.livenessProbe + livenessProbe = Optional.ofNullable(livenessProbe).orElse(new V1Probe()) .successThreshold(successThreshold) .failureThreshold(failureThreshold); } @@ -431,8 +476,16 @@ void fillInFrom(ServerPod serverPod1) { } envFrom.addAll(serverPod1.envFrom); } - livenessProbe.copyValues(serverPod1.livenessProbe); - readinessProbe.copyValues(serverPod1.readinessProbe); + if (livenessProbe == null) { + livenessProbe = serverPod1.livenessProbe; + } else { + copyValues(livenessProbe, serverPod1.livenessProbe); + } + if (readinessProbe == null) { + readinessProbe = serverPod1.readinessProbe; + } else { + copyValues(readinessProbe, serverPod1.readinessProbe); + } shutdown.copyValues(serverPod1.shutdown); for (V1Volume volume : serverPod1.getAdditionalVolumes()) { addIfMissing(new V1VolumeBuilder(volume).build()); diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/Validator.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/Validator.java index 09511687592..1d5fbd28654 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/Validator.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/Validator.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2023, Oracle and/or its affiliates. +// Copyright (c) 2017, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -18,6 +18,7 @@ import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1ContainerPort; import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1Probe; import io.kubernetes.client.openapi.models.V1VolumeMount; import oracle.kubernetes.operator.helpers.LegalNames; @@ -136,7 +137,7 @@ void verifyClusterContainerNameValid(ClusterResource cluster, String prefix) { .ifPresent(containers -> containers.forEach(container -> isContainerNameReserved(container, prefix))); } - void verifySuccessThresholdValue(ProbeTuning probe, String prefix) { + void verifySuccessThresholdValue(V1Probe probe, String prefix) { if (probe.getSuccessThreshold() != null && probe.getSuccessThreshold() != DEFAULT_SUCCESS_THRESHOLD) { failures.add(DomainValidationMessages.invalidLivenessProbeSuccessThresholdValue( probe.getSuccessThreshold(), prefix)); diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java index 950788e36ae..46342396a6f 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java @@ -230,6 +230,7 @@ public abstract class PodHelperTestBase extends DomainValidationTestBase { private static final int CONFIGURED_PERIOD = 35; public static final int CONFIGURED_FAILURE_THRESHOLD = 1; public static final int CONFIGURED_SUCCESS_THRESHOLD = 2; + public static final String CONFIGURED_PATH = "/path"; private static final Integer DEFAULT_SUCCESS_THRESHOLD = null; private static final String LOG_HOME = "/shared/logs"; private static final String NODEMGR_HOME = "/u01/nodemanager"; diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java index 314b98b548f..64d9e63d5a8 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java @@ -698,6 +698,12 @@ public ServerConfigurator withReadinessProbeThresholds(Integer successThreshold, return this; } + @Override + public ServerConfigurator withReadinessProbeHttpGetActionPath(String httpGetActionPath) { + server.setReadinessProbeHttpGetActionPath(httpGetActionPath); + return this; + } + @Override public ServerConfigurator withRequestRequirement(String resource, String quantity) { server.addRequestRequirement(resource, quantity); diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java index ed22b6b19e9..0db0f170821 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -42,6 +42,7 @@ import static oracle.kubernetes.operator.WebLogicConstants.JRF; import static oracle.kubernetes.operator.WebLogicConstants.WLS; import static oracle.kubernetes.operator.helpers.PodHelperTestBase.CONFIGURED_FAILURE_THRESHOLD; +import static oracle.kubernetes.operator.helpers.PodHelperTestBase.CONFIGURED_PATH; import static oracle.kubernetes.operator.helpers.PodHelperTestBase.CONFIGURED_SUCCESS_THRESHOLD; import static oracle.kubernetes.weblogic.domain.ChannelMatcher.channelWith; import static oracle.kubernetes.weblogic.domain.model.CreateIfNotExists.DOMAIN; @@ -379,7 +380,8 @@ private List createEnvFrom(String scope) { void readinessProbeSettings_returnsConfiguredValues() { configureServer(SERVER1) .withReadinessProbeSettings(INITIAL_DELAY, TIMEOUT, PERIOD) - .withReadinessProbeThresholds(CONFIGURED_SUCCESS_THRESHOLD, CONFIGURED_FAILURE_THRESHOLD); + .withReadinessProbeThresholds(CONFIGURED_SUCCESS_THRESHOLD, CONFIGURED_FAILURE_THRESHOLD) + .withReadinessProbeHttpGetActionPath(CONFIGURED_PATH); EffectiveServerSpec spec = info.getServer(SERVER1, CLUSTER_NAME); assertThat(spec.getReadinessProbe().getInitialDelaySeconds(), equalTo(INITIAL_DELAY)); @@ -387,6 +389,7 @@ void readinessProbeSettings_returnsConfiguredValues() { assertThat(spec.getReadinessProbe().getPeriodSeconds(), equalTo(PERIOD)); assertThat(spec.getReadinessProbe().getSuccessThreshold(), equalTo(CONFIGURED_SUCCESS_THRESHOLD)); assertThat(spec.getReadinessProbe().getFailureThreshold(), equalTo(CONFIGURED_FAILURE_THRESHOLD)); + assertThat(spec.getReadinessProbe().getHttpGet().getPath(), equalTo(CONFIGURED_PATH)); } @Test From 7f46f07ee1c45b01d6a4eafde3c0afe375b458b3 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 18 Apr 2024 10:34:34 -0400 Subject: [PATCH 034/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 686ab6b5368..152d8606b82 100644 --- a/pom.xml +++ b/pom.xml @@ -682,7 +682,7 @@ 2.0.1 2.0.1 1.0.39 - 1.6.0 + 1.7.0 1.4.0 1.4.0 1.16.1 @@ -699,7 +699,7 @@ 1.9.23 4.12.0 3.9.0 - 1.78 + 1.78.1 5.10.2 5.7.1 1.7.0 @@ -716,7 +716,7 @@ 2.10.1 9.1.0 2.0.13 - 1.5.5 + 1.5.6 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 4f2a892b77636ee878ed03503d24c3aeea0243da Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 18 Apr 2024 14:52:09 +0000 Subject: [PATCH 035/356] Merge branch 'fix-memsettings-for-wlst-startstop-script' into 'main' force the stopServer.sh to use less memory See merge request weblogic-cloud/weblogic-kubernetes-operator!4659 (cherry picked from commit 53e791d04cd2b816c1de8f7d667d85fd053d146d) 51be4296 force the stopServer.sh to use less memory when stopping server, otherwise it... d43aa045 The default from wlst should be sufficient --- operator/src/main/resources/scripts/stopServer.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/stopServer.sh b/operator/src/main/resources/scripts/stopServer.sh index 6faa63f6fd8..8cc40b2659b 100755 --- a/operator/src/main/resources/scripts/stopServer.sh +++ b/operator/src/main/resources/scripts/stopServer.sh @@ -113,7 +113,8 @@ check_for_shutdown [ ! -f "${SCRIPTPATH}/wlst.sh" ] && trace SEVERE "Missing file '${SCRIPTPATH}/wlst.sh'." && exit 1 trace "Before stop-server.py [${SERVER_NAME}] ${SCRIPTDIR}" &>> ${STOP_OUT_FILE} -${SCRIPTPATH}/wlst.sh /weblogic-operator/scripts/stop-server.py &>> ${STOP_OUT_FILE} +# Use the default wlst.sh memory settings for stopping the server -Xms32m -X1024m should be enough +USER_MEM_ARGS="" ${SCRIPTPATH}/wlst.sh /weblogic-operator/scripts/stop-server.py &>> ${STOP_OUT_FILE} trace "After stop-server.py" &>> ${STOP_OUT_FILE} # at this point node manager should have terminated the server From 4f1056241dcc0737c6292869b36a1c42a1edd3bf Mon Sep 17 00:00:00 2001 From: vanajakshi_mukkara Date: Sun, 21 Apr 2024 11:17:55 +0000 Subject: [PATCH 036/356] backport ItMultiDomainModelsScale podman fix --- .../oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java index 90a63652a34..4c3046be76b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java @@ -845,7 +845,7 @@ private void verifyReadyAppUsingIngressController(String domainUid, String domai private String createIngressHostRoutingIfNotExists(String domainNamespace, String domainUid) { - String ingressName = domainNamespace + "-" + domainUid + "-" + adminServerName; + String ingressName = domainNamespace + "-" + domainUid + "-" + adminServerName + "-" + 7001; String hostHeader = ""; try { List ingresses = listIngresses(domainNamespace); From 1cb07cb33db3cd8985fcaf78b07601eab30e699b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 22 Apr 2024 09:03:21 -0400 Subject: [PATCH 037/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 152d8606b82..811c5ef505f 100644 --- a/pom.xml +++ b/pom.xml @@ -662,7 +662,7 @@ 3.13.0 3.1.1 3.1.1 - 3.4.0 + 3.4.1 3.3.1 3.12.1 3.2.5 @@ -676,7 +676,7 @@ 10.15.0 1.0 3.3.2 - 3.2.3 + 3.2.4 2.0.0.0 1.3.3 2.0.1 From 42ee4227fcff9e3007a4b26af41e440234d5a8fe Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 22 Apr 2024 13:08:35 +0000 Subject: [PATCH 038/356] Merge branch 'wls-password-digest-fix' into 'main' Include SecurityConfiguration in the primordial domain to support PasswordDigest authentication. See merge request weblogic-cloud/weblogic-kubernetes-operator!4664 (cherry picked from commit 357127011ce795e3603587a09bdcf22a63fb3556) 1ed928e2 include SecurityConfiguration in the primordial domain to support PasswordDigest authentication. --- operator/src/main/resources/scripts/model-wdt-create-filter.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/operator/src/main/resources/scripts/model-wdt-create-filter.py b/operator/src/main/resources/scripts/model-wdt-create-filter.py index 37463bed365..e37e141edc4 100644 --- a/operator/src/main/resources/scripts/model-wdt-create-filter.py +++ b/operator/src/main/resources/scripts/model-wdt-create-filter.py @@ -40,6 +40,9 @@ def filter_model(model): if 'Security' in topology: model['topology']['Security'] = topology['Security'] + if 'SecurityConfiguration' in topology: + model['topology']['SecurityConfiguration'] = topology['SecurityConfiguration'] + if model and 'appDeployments' in model: model['appDeployments'] = {} From fdfa76471c04052659a56d2ee221e41243711803 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 22 Apr 2024 09:47:05 -0400 Subject: [PATCH 039/356] Prepare to release WKO 4.2.2 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 1e43524323a..aac39b728e4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index c190e1a6d29..c49f524ca9d 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 17e3c8f7e32..7055425466b 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 89015776c30..1df9f71e7ca 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.2-SNAPSHOT + 4.2.2 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 97fd9ef73fb..435dd39bca2 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 02148dfbdc5..90254b8a005 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.2-SNAPSHOT + 4.2.2 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index ecdd2ce6eb9..56ab885cab7 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 811c5ef505f..42d2b6f53c6 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 5f021ffdfb1..ab21562b621 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2-SNAPSHOT + 4.2.2 operator-swagger From e747c6f1aa6c9378682d00665b9842771346cfdb Mon Sep 17 00:00:00 2001 From: xian_cao Date: Mon, 22 Apr 2024 18:40:16 +0000 Subject: [PATCH 040/356] backport MR 4561 and 4615 to release/4.2 --- .../kubernetes/ItLBTwoDomainsNginx.java | 35 ++++++++++++++-- .../kubernetes/ItLBTwoDomainsTraefik.java | 36 ++++++++++++----- .../kubernetes/ItMiiDynamicUpdatePart2.java | 1 - .../kubernetes/ItMultiDomainModelsScale.java | 14 +++---- .../weblogic/kubernetes/TestConstants.java | 9 ++++- .../extensions/InitializationTasks.java | 25 ------------ .../kubernetes/utils/CommonLBTestUtils.java | 40 +++++++++---------- .../weblogic/kubernetes/utils/DeployUtil.java | 3 +- 8 files changed, 93 insertions(+), 70 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index d774622c5d5..e8f8850c8b0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -32,9 +32,17 @@ import org.junit.jupiter.api.TestMethodOrder; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.TestActions.createIngress; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; @@ -474,15 +482,19 @@ private static void createNginxTLSPathRoutingForTwoDomains() { * @return NGINX load balancer node port */ private static int getNginxLbNodePort(String channelName) { - String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; - - return getServiceNodePort(nginxNamespace, nginxServiceName, channelName); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + return channelName.equals("https") + ? ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT : ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT; + } else { + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + return getServiceNodePort(nginxNamespace, nginxServiceName, channelName); + } } private static void installNginxIngressController() { // install and verify Nginx logger.info("Installing Nginx controller using helm"); - nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + nginxHelmParams = installNginxLB(); // create ingress rules with non-tls host routing for NGINX createNginxIngressHostRoutingForTwoDomains(nginxHelmParams.getIngressClassName(), false); @@ -496,4 +508,19 @@ private static void installNginxIngressController() { // create ingress rules with TLS path routing for NGINX createNginxTLSPathRoutingForTwoDomains(); } + + private static NginxParams installNginxLB() { + + getLogger().info("Installing NGINX in namespace {0}", nginxNamespace); + + String nodePortValue = null; + if (!OKE_CLUSTER) { + nodePortValue = "NodePort"; + } + + NginxParams params = installAndVerifyNginx(nginxNamespace, ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT, + ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, nodePortValue); + + return params; + } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java index 3a096f46176..30791cd607e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java @@ -29,8 +29,13 @@ import org.junit.jupiter.api.TestMethodOrder; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; @@ -57,7 +62,6 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("oke-gate") - class ItLBTwoDomainsTraefik { private static final int numberOfDomains = 2; @@ -128,9 +132,14 @@ public static void initAll(@Namespaces(3) List namespaces) { // install Traefik ingress controller for all test cases using Traefik installTraefikIngressController(); - String ingressServiceName = traefikHelmParams.getReleaseName(); - ingressIP = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null - ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) : K8S_NODEPORT_HOST; + if (traefikHelmParams != null) { + String ingressServiceName = traefikHelmParams.getReleaseName(); + ingressIP = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null + ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) : K8S_NODEPORT_HOST; + } else { + logger.info("traefikHelmParams is null"); + ingressIP = K8S_NODEPORT_HOST; + } } /** @@ -222,7 +231,9 @@ private static void createCertKeyFiles(String cn) { private static void installTraefikIngressController() { // install and verify Traefik logger.info("Installing Traefik controller using helm"); - traefikHelmParams = installAndVerifyTraefik(traefikNamespace, 0, 0).getHelmParams(); + if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + traefikHelmParams = installAndVerifyTraefik(traefikNamespace, 0, 0).getHelmParams(); + } // create TLS secret for Traefik HTTPS traffic for (String domainUid : domainUids) { @@ -262,9 +273,16 @@ private static void createTraefikIngressRoutingRules(String domainNamespace) { } private int getTraefikLbNodePort(boolean isHttps) { - logger.info("Getting web node port for Traefik loadbalancer {0}", traefikHelmParams.getReleaseName()); - return assertDoesNotThrow(() -> - getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), isHttps ? "websecure" : "web"), - "Getting web node port for Traefik loadbalancer failed"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + return isHttps ? TRAEFIK_INGRESS_HTTPS_HOSTPORT : TRAEFIK_INGRESS_HTTP_HOSTPORT; + } else if (traefikHelmParams != null) { + logger.info("Getting web node port for Traefik loadbalancer {0}", traefikHelmParams.getReleaseName()); + return assertDoesNotThrow(() -> + getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), isHttps ? "websecure" : "web"), + "Getting web node port for Traefik loadbalancer failed"); + } else { + logger.info("failed to get Traefik Nodeport"); + return -1; + } } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java index c9d0abd1d7b..317c27bd945 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java @@ -117,7 +117,6 @@ public void beforeEach() { @DisplayName("Changing Weblogic datasource URL and deleting application with CommitUpdateAndRoll " + "using mii dynamic update") @Tag("gate") - @Tag("crio") void testMiiDeleteAppChangeDBUrlWithCommitUpdateAndRoll() { // This test uses the WebLogic domain created in BeforeAll method diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java index 4c3046be76b..3eac8512d06 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java @@ -267,7 +267,7 @@ void testScaleClustersByPatchingClusterResource(String domainType) { -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); - // verify admin console login using ingress controller + // verify ready app using ingress controller verifyReadyAppUsingIngressController(domainUid, domainNamespace); } @@ -329,7 +329,7 @@ void testScaleClustersWithRestApi(String domainType) { -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); - // verify admin console login using ingress controller + // verify ready app using ingress controller verifyReadyAppUsingIngressController(domainUid, domainNamespace); } @@ -393,7 +393,7 @@ void testScaleClustersWithWLDF(String domainType) { -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); - // verify admin console login using ingress controller + // verify ready app using ingress controller verifyReadyAppUsingIngressController(domainUid, domainNamespace); } @@ -795,7 +795,7 @@ private static void startDomainAndVerify(String domainNamespace, } } - // verify the admin console login using admin node port + // verify the ready app using admin node port private void verifyReadyAppUsingAdminNodePort(String domainUid, String domainNamespace) { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; @@ -816,7 +816,7 @@ private void verifyReadyAppUsingAdminNodePort(String domainUid, String domainNam "readyapp validation"); } - // Verify admin console login using ingress controller + // Verify ready app using ingress controller private void verifyReadyAppUsingIngressController(String domainUid, String domainNamespace) { if (!OKD) { @@ -845,13 +845,13 @@ private void verifyReadyAppUsingIngressController(String domainUid, String domai private String createIngressHostRoutingIfNotExists(String domainNamespace, String domainUid) { - String ingressName = domainNamespace + "-" + domainUid + "-" + adminServerName + "-" + 7001; + String ingressName = domainNamespace + "-" + domainUid + "-" + adminServerName + "-" + ADMIN_SERVER_PORT; String hostHeader = ""; try { List ingresses = listIngresses(domainNamespace); Optional ingressFound = ingresses.stream().filter(ingress -> ingress.equals(ingressName)).findAny(); if (ingressFound.isEmpty()) { - hostHeader = createIngressHostRouting(domainNamespace, domainUid, adminServerName, 7001); + hostHeader = createIngressHostRouting(domainNamespace, domainUid, adminServerName, ADMIN_SERVER_PORT); } } catch (Exception ex) { logger.severe(ex.getMessage()); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 243133eb50c..48a6bf117b1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -47,6 +47,7 @@ public interface TestConstants { public static final String WEBLOGIC_IMAGE_TAGS = getNonEmptySystemProperty("wko.it.weblogic.image.tags", DEFAULT_WEBLOGIC_IMAGE_TAGS); public static final int DEFAULT_MAX_CLUSTER_SIZE = 5; + public static final int ADMIN_SERVER_PORT_DEFAULT = 7001; // cluster constants public static final String CLUSTER_VERSION = @@ -478,7 +479,11 @@ public interface TestConstants { public static final int IT_EXTERNALLB_TUNNELING_HTTP_CONAINERPORT = 32169; public static final int IT_EXTERNALLB_TUNNELING_HTTP_HOSTPORT = 2172; public static final int IT_EXTERNALLB_TUNNELING_HTTPS_CONAINERPORT = 32170; - public static final int IT_EXTERNALLB_TUNNELING_HTTPS_HOSTPORT = 2173; - + public static final int IT_EXTERNALLB_TUNNELING_HTTPS_HOSTPORT = 2173; + + public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT = 30881; + public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT = 30444; + public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT = 2081; + public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT = 2444; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 834f0ff498f..982ff200b3a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -25,7 +25,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import oracle.weblogic.kubernetes.TestConstants; import oracle.weblogic.kubernetes.actions.impl.Namespace; -import oracle.weblogic.kubernetes.actions.impl.NginxParams; import oracle.weblogic.kubernetes.actions.impl.Operator; import oracle.weblogic.kubernetes.actions.impl.OperatorParams; import oracle.weblogic.kubernetes.actions.impl.TraefikParams; @@ -64,10 +63,6 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_WDT_MODEL_FILE; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTPS_NODEPORT; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTP_NODEPORT; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_NAMESPACE; import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; @@ -116,7 +111,6 @@ import static oracle.weblogic.kubernetes.utils.FileUtils.cleanupDirectory; import static oracle.weblogic.kubernetes.utils.IstioUtils.installIstio; import static oracle.weblogic.kubernetes.utils.IstioUtils.uninstallIstio; -import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyTraefik; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -650,25 +644,6 @@ private OperatorParams installWebHookOnlyOperator(String featureGates) { ); } - private void installNginxLB() { - deleteNamespace(NGINX_NAMESPACE); - assertDoesNotThrow(() -> new Namespace().name(NGINX_NAMESPACE).create()); - getLogger().info("Installing NGINX in namespace {0}", NGINX_NAMESPACE); - NginxParams params = installAndVerifyNginx(NGINX_NAMESPACE, NGINX_INGRESS_HTTP_NODEPORT, - NGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, "NodePort"); - assertDoesNotThrow(() -> Files.writeString(INGRESS_CLASS_FILE_NAME, params.getIngressClassName())); - String cmd = KUBERNETES_CLI + " get all -A"; - try { - ExecCommand.exec(cmd, true); - } catch (IOException | InterruptedException ex) { - getLogger().info("Exception in get all {0}", ex); - } - //TO-DO for OKD to use NGINX for all service access - //expose NGINX node port service and get route host - //oc -n ns-abcdef expose service nginx-release-nginx-ingress-nginx-controller - //oc -n ns-abcdef get routes nginx-release-nginx-ingress-nginx-controller '-o=jsonpath={.spec.host}' - } - private void installTraefikLB() { deleteNamespace(TRAEFIK_NAMESPACE); assertDoesNotThrow(() -> new Namespace().name(TRAEFIK_NAMESPACE).create()); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java index e8fce9af7c2..2ed9aa710ec 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java @@ -53,6 +53,7 @@ import static java.nio.file.Paths.get; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_PORT_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; @@ -60,6 +61,7 @@ import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; @@ -67,6 +69,8 @@ import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.getPodLog; @@ -79,6 +83,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.START_PORT; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; @@ -595,6 +600,12 @@ private static boolean deployApplication(String namespace, clusterViewAppPath, namespace),"Deploying the application"); return true; + } else if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + getLogger().info("Deploying webapp {0} to domain {1}", clusterViewAppPath, domainUid); + deployUsingWlst(adminServerPodName, Integer.toString(ADMIN_SERVER_PORT_DEFAULT), + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, "cluster-1", + clusterViewAppPath, namespace); + return true; } else { int serviceNodePort = assertDoesNotThrow(() -> getServiceNodePort(namespace, getExternalServicePodName(adminServerPodName), "default"), @@ -672,15 +683,12 @@ public static void verifyHeadersInAdminServerLog(String podName, String namespac public static void checkIngressReady(boolean isHostRouting, String ingressHost, boolean isTLS, int httpNodeport, int httpsNodeport, String pathString, String... ingressExtIP) { - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } + String host = ingressExtIP.length != 0 ? ingressExtIP[0] : K8S_NODEPORT_HOST; String hostAndPort; if (isTLS) { - hostAndPort = ingressExtIP.length != 0 ? ingressExtIP[0] : host + ":" + httpsNodeport; + hostAndPort = getHostAndPort(host, httpsNodeport); } else { - hostAndPort = ingressExtIP.length != 0 ? ingressExtIP[0] : host + ":" + httpNodeport; + hostAndPort = getHostAndPort(host, httpNodeport); } getLogger().info("hostAndPort to check ingress ready is: {0}", hostAndPort); @@ -769,17 +777,18 @@ public static void verifyClusterLoadbalancing(String domainUid, getLogger().info("Accessing the clusterview app through load balancer to verify all servers in cluster"); String curlRequest; String uri = "clusterview/ClusterViewServlet" + "\"?user=" + ADMIN_USERNAME_DEFAULT - + "&password=" + ADMIN_PASSWORD_DEFAULT + (host.contains(":") ? "&ipv6=true" : "&ipv6=false") + "\""; + + "&password=" + ADMIN_PASSWORD_DEFAULT + + ((host != null) && host.contains(":") ? "&ipv6=true" : "&ipv6=false") + "\""; if (hostRouting) { curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' " + "-H 'host: %s' %s://%s/" + uri, ingressHostName, protocol, host) : String.format("curl -g --show-error -ks --noproxy '*' " - + "-H 'host: %s' %s://%s:%s/" + uri, ingressHostName, protocol, host, lbPort); + + "-H 'host: %s' %s://%s/" + uri, ingressHostName, protocol, getHostAndPort(host, lbPort)); } else { curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' " + "%s://%s" + locationString + "/" + uri, protocol, host) : String.format("curl -g --show-error -ks --noproxy '*' " - + "%s://%s:%s" + locationString + "/" + uri, protocol, host, lbPort); + + "%s://%s" + locationString + "/" + uri, protocol, getHostAndPort(host, lbPort)); } List managedServers = new ArrayList<>(); @@ -826,18 +835,9 @@ public static void verifyAdminServerAccess(boolean isTLS, boolean isHostRouting, String ingressHostName, String pathLocation, - String... hostName) { + String hostName) { StringBuffer readyAppUrl = new StringBuffer(); - String hostAndPort; - if (hostName != null && hostName.length > 0) { - hostAndPort = OKE_CLUSTER_PRIVATEIP ? hostName[0] : hostName[0] + ":" + lbNodePort; - } else { - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } - hostAndPort = host + ":" + lbNodePort; - } + String hostAndPort = OKE_CLUSTER_PRIVATEIP ? hostName : getHostAndPort(hostName, lbNodePort); if (isTLS) { readyAppUrl.append("https://"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java index b141e862c7b..22f87004c4b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -76,7 +76,6 @@ public static void deployUsingWlst(String host, String port, String userName, // this secret is used only for non-kind cluster createBaseRepoSecret(namespace); - // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("domain", "properties"), "Creating domain properties file failed"); From a3d56674f42e7346a7f4ed831105f7e2e80cc5fb Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 22 Apr 2024 15:10:30 -0400 Subject: [PATCH 041/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index aac39b728e4..b16de6bb5ac 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index c49f524ca9d..371d0bfb59e 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 7055425466b..8a1fb572c59 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 1df9f71e7ca..1e2f346f7a6 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.2 + 4.2.3-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 435dd39bca2..35d52d62fa1 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 90254b8a005..a9813f591b2 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.2 + 4.2.3-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 56ab885cab7..28dec951c52 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 42d2b6f53c6..cb18d257baf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index ab21562b621..8395d7d4788 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.2 + 4.2.3-SNAPSHOT operator-swagger From 9b6b4d8beb118eeebd720abc7300163f779724a1 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Tue, 23 Apr 2024 17:32:25 +0000 Subject: [PATCH 042/356] [podman] backport MR 4621 to release/4.2 --- .../kubernetes/ItValidateWebhookReplicas.java | 18 ++++++-- .../kubernetes/ItWDTModelNoServer.java | 45 +++++++++++++++++-- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java index cff28cef056..9a13e3bdb84 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -45,17 +45,21 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_REPO; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_EXTERNAL_REST_HTTPSPORT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_PASSWORD; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_USERNAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.WLS_DOMAIN_TYPE; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.getDomainCustomResource; @@ -146,9 +150,15 @@ public static void initAll(@Namespaces(3) List namespaces) { // install and verify operator with REST API logger.info("Install an operator in namespace {0}, managing namespace {1} and {2}", opNamespace, domainNamespace, domainNamespace2); - installAndVerifyOperator(opNamespace, opServiceAccount, true, 0, - domainNamespace, domainNamespace2); - externalRestHttpsPort = getServiceNodePort(opNamespace, "external-weblogic-operator-svc"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + installAndVerifyOperator(opNamespace, opServiceAccount, true, OPERATOR_EXTERNAL_REST_HTTPSPORT, + domainNamespace, domainNamespace2); + externalRestHttpsPort = OPERATOR_EXTERNAL_REST_HTTPSPORT; + } else { + installAndVerifyOperator(opNamespace, opServiceAccount, true, 0, + domainNamespace, domainNamespace2); + externalRestHttpsPort = getServiceNodePort(opNamespace, "external-weblogic-operator-svc"); + } // create a mii domain resource with one cluster logger.info("Create model-in-image domain {0} in namespace {1}, and wait until it comes up", diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWDTModelNoServer.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWDTModelNoServer.java index 68309e462e1..dbacffd0d06 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWDTModelNoServer.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWDTModelNoServer.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -7,6 +7,8 @@ import java.util.Collections; import java.util.List; +import oracle.weblogic.kubernetes.actions.impl.primitive.Command; +import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -16,9 +18,15 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.TestActions.deleteClusterCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.getOperatorPodName; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; @@ -69,6 +77,7 @@ class ItWDTModelNoServer { private static final String MII_IMAGE_NAME = "wdtmodelnoserver-mii"; private static final int replicaCount = 2; private static final String internalPort = "8001"; + private static final int ADMIN_SERVER_PORT = 7001; private static final String appPath = "sample-war/index.jsp"; private static final String clusterName = "cluster-1"; @@ -532,9 +541,37 @@ private void checkAdminServerName(String adminServerPodName, String expectedAdmi int adminServiceNodePort = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid"); - assertTrue(checkSystemResourceDomainConfig(adminSvcExtHost, adminServiceNodePort, - "\"adminServerName\": \"" + expectedAdminServerName + "\""), - "Admin server name is not '" + expectedAdminServerName + "'"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + assertTrue(checkSystemResourceConfigViaAdminPod(adminServerPodName, + "\"adminServerName\": \"" + expectedAdminServerName + "\""), + "Admin server name is not '" + expectedAdminServerName + "'"); + } else { + assertTrue(checkSystemResourceDomainConfig(adminSvcExtHost, adminServiceNodePort, + "\"adminServerName\": \"" + expectedAdminServerName + "\""), + "Admin server name is not '" + expectedAdminServerName + "'"); + } logger.info("AdminServerName is {0}", expectedAdminServerName); } + + private boolean checkSystemResourceConfigViaAdminPod(String adminServerPodName, + String expectedValue) { + final LoggingFacade logger = getLogger(); + + StringBuffer curlString = new StringBuffer(KUBERNETES_CLI + " exec -n " + + domainNamespace + " " + adminServerPodName) + .append(" -- /bin/bash -c \"") + .append("curl -g --user ") + .append(ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT) + .append(" http://" + adminServerPodName + ":" + ADMIN_SERVER_PORT) + .append("/management/weblogic/latest/domainConfig") + .append("/") + .append(" \""); + + logger.info("checkSystemResource: curl command {0}", new String(curlString)); + return Command + .withParams(new CommandParams() + .command(curlString.toString())) + .executeAndVerify(expectedValue); + } + } From 24ffab4f2261c3ed1a5c1f6cd93c4ba6b339bc4f Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Wed, 24 Apr 2024 18:27:00 +0000 Subject: [PATCH 043/356] Backported internal OKE conversion in OWLS-112085 on main into release/4.2 --- Jenkinsfile.oke | 13 +++--- .../kubernetes/ItManagedCoherence.java | 43 +++++++++++++------ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 050f1063099..2aaea4f42b2 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -3,15 +3,18 @@ // CRON_SETTINGS = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false - H 1 * * * % MAVEN_PROFILE_NAME=oke-parallelnew;CLUSTER_NAME=parone;PARALLEL_RUN=true''' - + H 2 * * * % MAVEN_PROFILE_NAME=oke-parallelnew;CLUSTER_NAME=parone;PARALLEL_RUN=true''' pipeline { agent { label 'large' } options { timeout(time: 1800, unit: 'MINUTES') //disableConcurrentBuilds() } - + triggers { + // timer trigger for "nightly build" + parameterizedCron(env.JOB_NAME == 'wko-oke-nightly' ? + CRON_SETTINGS : '') + } tools { maven 'maven-3.8.7' jdk 'jdk21' @@ -476,13 +479,13 @@ EOF environment { runtime_path = "${WORKSPACE}/bin:${PATH}" clusterName = "${CLUSTER_NAME}" - FSS_DIR = "/oketest1,/oketest2,/oketest3" + FSS_DIR = "/oketest1,/oketest2,/oketest3,/oketest4,/oketest5,/oketest6,/oketest7,/oketest8,/oketest9,/oketest10,/oketest11,/oketest12,/oketest13,/oketest14,/oketest15" } steps { script { def res = 0 - currentBuild.description = "${GIT_BRANCH} ${MAVEN_PROFILE_NAME}" + currentBuild.description = "${GIT_BRANCH} ${MAVEN_PROFILE_NAME} ${CLUSTER_NAME}" res = sh(script: ''' if [ -z "${IT_TEST}" ] && [ "${MAVEN_PROFILE_NAME}" = "integration-tests" ]; then echo 'ERROR: All tests cannot be run with integration-tests profile' diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java index 42481faccf5..6a19a74d617 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java @@ -7,6 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +29,7 @@ import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.BuildApplication; +import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; @@ -39,8 +41,10 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.INGRESS_CLASS_FILE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.OKD; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; @@ -74,7 +78,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-sequential1") +@Tag("oke-gate") class ItManagedCoherence { // constants for Coherence @@ -129,10 +133,15 @@ public static void init(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace list is null"); domainNamespace = namespaces.get(2); + String nodePortValue = null; + if (!OKE_CLUSTER) { + nodePortValue = "NodePort"; + } + // install and verify Traefik if not running on OKD if (!OKD || (TestConstants.KIND_CLUSTER && TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT))) { - traefikParams = installAndVerifyTraefik(traefikNamespace, 0, 0, "NodePort"); + traefikParams = installAndVerifyTraefik(traefikNamespace, 0, 0, nodePortValue); traefikHelmParams = traefikParams.getHelmParams(); } @@ -165,6 +174,16 @@ void testMultiClusterCoherenceDomain() throws IOException { // create and verify a two-cluster WebLogic domain with a Coherence cluster createAndVerifyDomain(domImage); + String command = KUBERNETES_CLI + " get all --all-namespaces"; + logger.info("curl command to get all --all-namespaces is: {0}", command); + + try { + ExecResult result0 = ExecCommand.exec(command, true); + logger.info("result is: {0}", result0.toString()); + } catch (IOException | InterruptedException ex) { + ex.printStackTrace(); + } + if (OKD) { String cluster1HostName = domainUid + "-cluster-cluster-1"; final String cluster1IngressHost = createRouteForOKD(cluster1HostName, domainNamespace); @@ -174,12 +193,12 @@ void testMultiClusterCoherenceDomain() throws IOException { -> coherenceCacheTest(cluster1IngressHost, 0), "Test Coherence cache failed"); assertTrue(testCompletedSuccessfully, "Test Coherence cache failed"); } else { - Map clusterNameMsPortMap = new HashMap<>(); for (int i = 1; i <= NUMBER_OF_CLUSTERS; i++) { clusterNameMsPortMap.put(CLUSTER_NAME_PREFIX + i, MANAGED_SERVER_PORT); } - String hostHeader = domainUid + "." + domainNamespace + ".cluster-1.test"; + + String clusterHostname = domainUid + "." + domainNamespace + ".cluster-1.test"; String hostAndPort; int ingressServiceNodePort; if (TestConstants.KIND_CLUSTER @@ -191,10 +210,12 @@ void testMultiClusterCoherenceDomain() throws IOException { hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; ingressServiceNodePort = TRAEFIK_INGRESS_HTTP_HOSTPORT; } else { - // clusterNameMsPortMap.put(clusterName, managedServerPort); + List domainUids = new ArrayList<>(); + domainUids.add(domainUid); + logger.info("Creating ingress for domain {0} in namespace {1}", domainUid, domainNamespace); - createTraefikIngressForDomainAndVerify(domainUid, domainNamespace, 0, clusterNameMsPortMap, true, null, - traefikParams.getIngressClassName()); + createTraefikIngressForDomainAndVerify(domainUid, domainNamespace, 0, + clusterNameMsPortMap, true, null, traefikParams.getIngressClassName()); // get ingress service Name and Nodeport String ingressServiceName = traefikHelmParams.getReleaseName(); @@ -207,13 +228,13 @@ void testMultiClusterCoherenceDomain() throws IOException { hostAndPort = getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) != null ? getServiceExtIPAddrtOke(ingressServiceName, traefikNamespace) - : getHostAndPort(hostHeader, ingressServiceNodePort); + : getHostAndPort(clusterHostname, ingressServiceNodePort); } - assertTrue(checkCoheranceApp(hostAndPort, hostHeader), "Failed to access Coherance Application"); + assertTrue(checkCoheranceApp(hostAndPort, clusterHostname), "Failed to access Coherance Application"); // test adding data to the cache and retrieving them from the cache boolean testCompletedSuccessfully = assertDoesNotThrow(() - -> coherenceCacheTest(hostHeader, ingressServiceNodePort), "Test Coherence cache failed"); + -> coherenceCacheTest(clusterHostname, ingressServiceNodePort), "Test Coherence cache failed"); assertTrue(testCompletedSuccessfully, "Test Coherence cache failed"); } } @@ -486,7 +507,6 @@ private ExecResult clearCache(String hostName, String hostAndPort) { } private boolean checkCoheranceApp(String hostAndPort, String hostHeader) { - StringBuffer curlCmd = new StringBuffer("curl -g --silent --show-error --noproxy '*' "); curlCmd .append("-d 'action=clear' ") @@ -509,5 +529,4 @@ private boolean checkCoheranceApp(String hostAndPort, String hostHeader) { curlCmd); return true; } - } From 04659ff15a0749bc337bc9619c8120018d1fd6ae Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Thu, 25 Apr 2024 13:03:49 +0000 Subject: [PATCH 044/356] Backport oke-dev Jenkins job setup --- Jenkinsfile.oke | 52 +- integration-tests/pom.xml | 14 - .../weblogic/kubernetes/ItCoherenceTests.java | 2 +- .../ItConsensusLeasingValidation.java | 2 +- .../kubernetes/ItDataHomeOverride.java | 2 +- ...DiagnosticsCompleteAvailableCondition.java | 2 +- .../ItDiagnosticsFailedCondition.java | 2 +- .../weblogic/kubernetes/ItElasticLogging.java | 2 +- .../kubernetes/ItElasticLoggingFluentd.java | 2 +- .../kubernetes/ItEvictedPodsCycling.java | 2 +- .../weblogic/kubernetes/ItInitContainers.java | 2 +- .../kubernetes/ItIstioCoherenceTests.java | 2 +- .../ItIstioCrossDomainTransaction.java | 2 +- .../kubernetes/ItIstioDomainInImage.java | 2 +- .../kubernetes/ItIstioDomainInPV.java | 2 +- .../ItIstioGatewaySessionMigration.java | 2 +- .../weblogic/kubernetes/ItIstioMiiDomain.java | 2 +- .../ItIstioProductionSecureMode.java | 2 +- .../kubernetes/ItIstioSessionMigration.java | 2 +- .../kubernetes/ItIstioTwoDomainsInImage.java | 2 +- .../kubernetes/ItKubernetesDomainEvents.java | 2 +- .../ItKubernetesNameSpaceWatchingEvents.java | 2 +- .../kubernetes/ItLogHomeFlatStructure.java | 2 +- .../kubernetes/ItManageNameSpace.java | 2 +- .../kubernetes/ItMiiAuxiliaryImage.java | 2 +- .../ItMiiCreateAuxImageWithImageTool.java | 2 +- .../kubernetes/ItMiiCustomSslStore.java | 2 +- .../weblogic/kubernetes/ItMiiDomain.java | 2 +- .../kubernetes/ItMiiDomainModelInPV.java | 2 +- .../weblogic/kubernetes/ItMiiMultiModel.java | 2 +- .../kubernetes/ItMiiUpdateDomainConfig.java | 2 +- .../kubernetes/ItMultiDomainModels.java | 2 +- .../kubernetes/ItOCILoadBalancer.java | 2 +- .../weblogic/kubernetes/ItPodTemplates.java | 2 +- .../weblogic/kubernetes/ItPodsRestart.java | 2 +- .../kubernetes/ItPodsShutdownOption.java | 2 +- .../kubernetes/ItProductionSecureMode.java | 2 +- .../kubernetes/ItRecoveryDomainInPV.java | 2 +- .../weblogic/kubernetes/ItRemoteConsole.java | 2 +- .../kubernetes/ItRetryImprovements.java | 2 +- .../kubernetes/ItServerStartPolicy.java | 2 +- .../ItServerStartPolicyConfigCluster.java | 2 +- .../ItServerStartPolicyDynamicCluster.java | 2 +- .../kubernetes/ItSessionMigration.java | 2 +- .../weblogic/kubernetes/ItStickySession.java | 2 +- .../kubernetes/ItSystemResOverrides.java | 2 +- .../kubernetes/ItValidateWebhookReplicas.java | 2 +- .../weblogic/kubernetes/ItWlsDomainOnPV.java | 2 +- .../resources/oke/terraform/okeint/cluster.tf | 37 +- .../resources/oke/terraform/okeint/export.tf | 2 +- .../oke/terraform/okeint/export_set.tf | 2 +- .../oke/terraform/okeint/file_system.tf | 32 +- .../oke/terraform/okeint/kube_config.tf | 2 +- .../oke/terraform/okeint/mount_target.tf | 6 +- .../oke/terraform/okeint/node-pool.tf | 48 +- .../oke/terraform/okeint/provider.tf | 2 +- .../resources/oke/terraform/okeint/vcn.tf | 494 ++++++++++++++++++ .../oke/terraform/okeint/versions.tf | 2 +- .../oke/terraform/okeintdev/cluster.tf | 117 +++++ .../oke/terraform/okeintdev/export.tf | 17 + .../oke/terraform/okeintdev/export_set.tf | 10 + .../oke/terraform/okeintdev/file_system.tf | 15 + .../oke/terraform/okeintdev/kube_config.tf | 23 + .../oke/terraform/okeintdev/node-pool.tf | 76 +++ .../oke/terraform/okeintdev/oke.create.sh | 195 +++++++ .../oke/terraform/okeintdev/oke.delete.sh | 82 +++ .../oke/terraform/okeintdev/provider.tf | 40 ++ .../oke/terraform/okeintdev/template.tfvars | 53 ++ .../oke/terraform/okeintdev/versions.tf | 8 + 69 files changed, 1246 insertions(+), 175 deletions(-) create mode 100755 integration-tests/src/test/resources/oke/terraform/okeint/vcn.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/cluster.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/export.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/export_set.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/file_system.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/kube_config.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okeintdev/node-pool.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/oke.create.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okeintdev/oke.delete.sh create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/provider.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okeintdev/template.tfvars create mode 100644 integration-tests/src/test/resources/oke/terraform/okeintdev/versions.tf diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 2aaea4f42b2..04746f68781 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -3,7 +3,7 @@ // CRON_SETTINGS = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false - H 2 * * * % MAVEN_PROFILE_NAME=oke-parallelnew;CLUSTER_NAME=parone;PARALLEL_RUN=true''' + H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' pipeline { agent { label 'large' } options { @@ -57,6 +57,7 @@ pipeline { wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" kubeconfig_file = "${WORKSPACE}/terraform/${CLUSTER_NAME}_kubeconfig" availability_domain = "${env.JOB_NAME == 'wko-oke-nightly-parallel' ? 'mFEn:PHX-AD-1' : 'mFEn:PHX-AD-1'}" + oke_run = "${env.JOB_NAME == 'wko-oke-nightly' ? 'okeint' : 'okeintdev'}" } @@ -74,13 +75,13 @@ pipeline { string(name: 'OKE_KUBE_VERSION', description: 'kube version for oke cluster', - defaultValue: '1.28.2' + defaultValue: '1.29.1' ) string(name: 'IMAGE_ID', description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', //defaultValue OKE1.26.2: 'ocid1.image.oc1.phx.aaaaaaaaaizmtmozeudeeuq7o5ir7dkl2bkxbbb3tgomshqbqn6jpomrsjza' //1.27.2 oke defaultValue: 'ocid1.image.oc1.phx.aaaaaaaaypr5r5drojwytghw6e6mvpjsscrnkuwtmqlmvmix7kjb2zcnc7wa' - defaultValue: 'ocid1.image.oc1.phx.aaaaaaaajqv2odwgofwohn2chxfytl4uiupz2filik5e2rrepe3izq26pyrq' + defaultValue: 'ocid1.image.oc1.phx.aaaaaaaa22u45gr3ikxnc6rius2qil2kz5k3e7p476c4usr6qnvql4l5dxea' ) string(name: 'KUBECTL_VERSION', @@ -103,26 +104,22 @@ pipeline { '1.9.9' ] ) - choice(name: 'CLUSTER_NAME', - description: 'Oke cluster name.', - choices: [ - 'parone', - 'seqone', - 'seqtwo' - ] - ) + choice(name: 'MAVEN_PROFILE_NAME', - description: 'Profile to use in mvn command to run the tests. Possible values are oke-gate, oke-sequential1,oke-parallelnew. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + description: 'Profile to use in mvn command to run the tests. Possible values are oke-gate,oke-parallel. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', choices: [ 'oke-gate', - 'oke-sequential1', - 'oke-parallelnew' + 'oke-parallel' ] ) string(name: 'NUMBER_OF_THREADS', description: 'Number of threads to run the classes in parallel, default is 3.', defaultValue: "3" ) + string(name: 'CLUSTER_NAME', + description: 'OKE cluster name', + defaultValue: "seqone" + ) string(name: 'WDT_DOWNLOAD_URL', description: 'URL to download WDT.', //defaultValue: 'https://github.com/oracle/weblogic-deploy-tooling/releases/latest' @@ -139,6 +136,10 @@ pipeline { description: '', defaultValue: "VM.Standard.E3.Flex" ) + string(name: 'MOUNT_TARGET_OCID', + description: 'only for debug runs on wko-oke-dev', + defaultValue: "test" + ) string(name: 'BRANCH', description: '', defaultValue: "main" @@ -351,6 +352,7 @@ vcn.ocid=${vcn_ocid} pub.subnet.ocid=${pub_subnet_ocid} private.subnet.ocid=${private_subnet_ocid} nodepool.shape=${NODE_SHAPE} +mounttarget.ocid=${MOUNT_TARGET_OCID} nodepool.imagename=${IMAGE_ID} k8s.version=v${OKE_KUBE_VERSION} nodepool.ssh.pubkey=${ssh_pubkey} @@ -393,7 +395,7 @@ EOF OCI_PROP_FILE="${WORKSPACE}/terraform/oci.prop" CLUSTER_NAME="${CLUSTER_NAME}" KUBECONFIG ="${WORKSPACE}/terraform/${CLUSTER_NAME}_kubeconfig" - terraform_script_dir_name = "${WORKSPACE}/integration-tests/src/test/resources/oke/terraform/okeint" + terraform_script_dir_name = "${WORKSPACE}/integration-tests/src/test/resources/oke/terraform/${oke_run}" } steps { @@ -413,7 +415,7 @@ EOF mkdir -p ${WORKSPACE}/terraform/terraforminstall sh ${WORKSPACE}/terraform/oke.create.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"$CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} @@ -454,9 +456,15 @@ EOF compartment_ocid=${compartment_id} echo "creating storage class to setup OFSS ..." echo "getting MountTarget ID" - mount_target_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | .id'` + if [ "${JOB_NAME}" = "wko-oke-nightly" ]; then + mount_target_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | .id'` + else + mount_target_id=${MOUNT_TARGET_OCID} + fi + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" @@ -479,7 +487,8 @@ EOF environment { runtime_path = "${WORKSPACE}/bin:${PATH}" clusterName = "${CLUSTER_NAME}" - FSS_DIR = "/oketest1,/oketest2,/oketest3,/oketest4,/oketest5,/oketest6,/oketest7,/oketest8,/oketest9,/oketest10,/oketest11,/oketest12,/oketest13,/oketest14,/oketest15" + FSS_DIR = "${env.JOB_NAME == 'wko-oke-nightly' ? '/oketest1,/oketest2,/oketest3,/oketest4,/oketest5,/oketest6,/oketest7,/oketest8,/oketest9,/oketest10,/oketest11,/oketest12,/oketest13,/oketest14,/oketest15' : '/${clusterName}oketest1,/${clusterName}oketest2'}" + } steps { @@ -534,8 +543,13 @@ EOF NODE_IP=`kubectl get nodes -o wide| awk '{print $7}'| head -n2 | tail -n1` echo "second node external IP ${NODE_IP}" export NODE_IP=${NODE_IP} + if [ "${JOB_NAME}" = "wko-oke-nightly" ]; then + echo "Mount Target setup for wko-oke-nightly" + mt_privateip_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | ."private-ip-ids"[]'` - mt_privateip_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | ."private-ip-ids"[]'` + else + mt_privateip_id=`oci fs mount-target get --mount-target-id=${MOUNT_TARGET_OCID} | jq -r '.data| ."private-ip-ids"[]'` + fi # Check if the mt_privateip_id is an array if [ "$(declare -p mt_privateip_id 2>/dev/null | grep -o 'declare -a')" == "declare -a" ]; then # Select first diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 8a1fb572c59..43267feeee7 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -474,20 +474,6 @@ oke-gate - - oke-sequential1 - - false - oke-sequential1 - - - - oke-parallelnew - - false - oke-parallelnew - - oke-arm diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java index f1bf4b14ed5..6492d4a70b4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java @@ -72,7 +72,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItCoherenceTests { // constants for Coherence diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConsensusLeasingValidation.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConsensusLeasingValidation.java index 511c2984d25..8c4a45c5ca6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConsensusLeasingValidation.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConsensusLeasingValidation.java @@ -39,7 +39,7 @@ @Tag("kind-parallel") @Tag("olcne-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItConsensusLeasingValidation { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDataHomeOverride.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDataHomeOverride.java index 67785966c47..f38b1bb4075 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDataHomeOverride.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDataHomeOverride.java @@ -47,7 +47,7 @@ @Tag("kind-parallel") @Tag("olcne-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItDataHomeOverride { // domain constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java index b38ede05812..63d64d20c2d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsCompleteAvailableCondition.java @@ -62,7 +62,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItDiagnosticsCompleteAvailableCondition { private static final String cluster1Name = "cluster-1"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java index 9ddc25fd889..8856d1111f8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java @@ -110,7 +110,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItDiagnosticsFailedCondition { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLogging.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLogging.java index 54b17d8a082..e13a6c5b6d9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLogging.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLogging.java @@ -101,7 +101,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItElasticLogging { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java index 21398bf0001..11230c0a705 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java @@ -113,7 +113,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItElasticLoggingFluentd { // constants for creating domain image using model in image diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java index f0ec1af9b39..e57542270ac 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java @@ -54,7 +54,7 @@ @IntegrationTest @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItEvictedPodsCycling { // constants for Domain diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java index c42948187dc..94cc4dcb50e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java @@ -77,7 +77,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItInitContainers { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java index 2dc495d1493..6c78264900e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java @@ -64,7 +64,7 @@ @DisplayName("Test to create a WebLogic domain with Coherence and verify the use of Coherence cache service") @IntegrationTest @Tag("kind-parallel") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioCoherenceTests { // constants for Coherence diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java index 83e853df61f..184114b095f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java @@ -89,7 +89,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioCrossDomainTransaction { private static final String WDT_MODEL_FILE_DOMAIN1 = "model-crossdomaintransaction-domain1.yaml"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java index 2779222adb6..ab37a499d64 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java @@ -77,7 +77,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioDomainInImage { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java index 11b44b2d2ae..cee503830af 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java @@ -108,7 +108,7 @@ @Tag("kind-parallel") @Tag("olcne-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioDomainInPV { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java index 63be97cf84c..fb3002dc20d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java @@ -63,7 +63,7 @@ @Tag("kind-parallel") @Tag("olcne-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioGatewaySessionMigration { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java index f0a8e53b921..78b55807d55 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java @@ -88,7 +88,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("olcne-srg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioMiiDomain { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java index 2bf48adf4e6..27ce7963d89 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java @@ -59,7 +59,7 @@ @Tag("kind-parallel") @Tag("oke-arm") @Tag("olcne-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioProductionSecureMode { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java index 7057cc8fb93..f60ae815648 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java @@ -42,7 +42,7 @@ @Tag("kind-parallel") @Tag("oke-arm") @Tag("olcne-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioSessionMigration { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java index 5acc7a19af1..55695f56065 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java @@ -71,7 +71,7 @@ @DisplayName("Test to create two WebLogic domains in domainhome-in-image model with istio configuration") @IntegrationTest @Tag("kind-parallel") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioTwoDomainsInImage { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index 120b2b59c1c..256367a51fb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -133,7 +133,7 @@ @Tag("oke-arm") @IntegrationTest @Tag("olcne-srg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItKubernetesDomainEvents { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesNameSpaceWatchingEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesNameSpaceWatchingEvents.java index f019ce0d542..16ae4816bfc 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesNameSpaceWatchingEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesNameSpaceWatchingEvents.java @@ -54,7 +54,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItKubernetesNameSpaceWatchingEvents { private static final String newNSWithoutLabels = "ns-newnamespace1"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java index f84e71bc55a..88f36dfc871 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java @@ -85,7 +85,7 @@ @IntegrationTest @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItLogHomeFlatStructure { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java index 765de0daa50..ff91078cc96 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java @@ -86,7 +86,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItManageNameSpace { private static String[] opNamespaces = new String[4]; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index f1252d8db08..115b149ed17 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -121,7 +121,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMiiAuxiliaryImage { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCreateAuxImageWithImageTool.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCreateAuxImageWithImageTool.java index 1610d126074..abc1c256d8d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCreateAuxImageWithImageTool.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCreateAuxImageWithImageTool.java @@ -77,7 +77,7 @@ @Tag("okd-wls-srg") @Tag("olcne-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMiiCreateAuxImageWithImageTool { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java index 0a98be7e85f..c4eba945e3c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java @@ -70,7 +70,7 @@ @IntegrationTest @Tag("olcne-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMiiCustomSslStore { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java index c90828f5b3e..c1dea3d6cc0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java @@ -137,7 +137,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-srg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMiiDomain { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java index d65ece16836..ed9332d2256 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java @@ -102,7 +102,7 @@ @Tag("kind-parallel") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") public class ItMiiDomainModelInPV { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java index 3ded1e07dcd..9f39ad26492 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java @@ -76,7 +76,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMiiMultiModel { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index 760b046f16b..c1733cdaddb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -119,7 +119,7 @@ @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-srg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMiiUpdateDomainConfig { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java index 472f4e15762..77cda573f93 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java @@ -78,7 +78,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItMultiDomainModels { // domain constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java index 9997f30fced..b859da6e8e1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java @@ -45,7 +45,7 @@ + "all managed servers in the domain through OCI Load Balancer") @IntegrationTest @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItOCILoadBalancer { // domain constants private static final int replicaCount = 2; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java index ced31337083..32f6f4f980b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java @@ -63,7 +63,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItPodTemplates { // domain constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java index 0cebb66e7e9..22bc20061e0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java @@ -91,7 +91,7 @@ @Tag("kind-parallel") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItPodsRestart { private static String miiImage; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java index 09541cf30ab..33f4dbca322 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java @@ -82,7 +82,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItPodsShutdownOption { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java index 9f32208ada4..f6424590656 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java @@ -102,7 +102,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItProductionSecureMode { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java index f1a4f204ea9..7f7a56b0ce2 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java @@ -79,7 +79,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItRecoveryDomainInPV { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java index 512f79b2bb9..61c691e8981 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java @@ -94,7 +94,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItRemoteConsole { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRetryImprovements.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRetryImprovements.java index 07ce9a07df8..0dbb4366cb7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRetryImprovements.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRetryImprovements.java @@ -84,7 +84,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItRetryImprovements { // domain constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicy.java index 0f23c320240..a8b13a82fec 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicy.java @@ -65,7 +65,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItServerStartPolicy { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 74d0e5baf77..8fd03d580bb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -62,7 +62,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItServerStartPolicyConfigCluster { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyDynamicCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyDynamicCluster.java index 1ac514adb1a..8b057005d48 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyDynamicCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyDynamicCluster.java @@ -66,7 +66,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItServerStartPolicyDynamicCluster { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java index d31bb358456..7ccdcf174df 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java @@ -76,7 +76,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItSessionMigration { // constants for creating domain image using model in image diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java index a08b6822599..e7609fa3957 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java @@ -83,7 +83,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItStickySession { // constants for creating domain image using model in image diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index d1441f4b6f2..69a96c12c68 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -106,7 +106,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItSystemResOverrides { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java index 9a13e3bdb84..b56cf774899 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java @@ -101,7 +101,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItValidateWebhookReplicas { private static String opNamespace = null; private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java index eb1ded888eb..f800fe8209d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java @@ -64,7 +64,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("olcne-mrg") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItWlsDomainOnPV { private static String domainNamespace = null; diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/cluster.tf b/integration-tests/src/test/resources/oke/terraform/okeint/cluster.tf index 450741ed604..69b886d0f49 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/cluster.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/cluster.tf @@ -1,18 +1,7 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ - -// Compartment in which to create the cluster resources. -variable "compartment_name" { -} - -variable "compartment_ocid" { -} - -variable "sub_compartment_ocid" { -} - variable "cluster_kubernetes_version" { default = "v1.17.9" } @@ -62,24 +51,14 @@ variable "node_pool_node_shape" { } variable "node_pool_quantity_per_subnet" { - default = 1 + default = 2 } variable "node_pool_ssh_public_key" { } -variable "vcn_ocid" { -} - -variable "pub_subnet_ocid" { -} - -variable "cluster_cluster_pod_network_options_cni_type" { - default = "OCI_VCN_IP_NATIVE" -} - data "oci_identity_availability_domains" "tfsample_availability_domains" { - compartment_id = var.sub_compartment_ocid + compartment_id = var.compartment_ocid } resource "oci_containerengine_cluster" "tfsample_cluster" { @@ -87,17 +66,11 @@ resource "oci_containerengine_cluster" "tfsample_cluster" { compartment_id = var.compartment_ocid kubernetes_version = var.cluster_kubernetes_version name = var.cluster_name - vcn_id = var.vcn_ocid - - endpoint_config { + vcn_id = oci_core_virtual_network.oke-vcn.id - #Optional - is_public_ip_enabled = "true" - subnet_id = var.pub_subnet_ocid - } #Optional options { - service_lb_subnet_ids = [var.pub_subnet_ocid] + service_lb_subnet_ids = [oci_core_subnet.oke-subnet-loadbalancer-1.id, oci_core_subnet.oke-subnet-loadbalancer-2.id] #Optional add_ons { diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/export.tf b/integration-tests/src/test/resources/oke/terraform/okeint/export.tf index 75b09bd83e2..e0b37082ffc 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/export.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/export.tf @@ -1,5 +1,5 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ resource "oci_file_storage_export" "oketest_export1" { diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/export_set.tf b/integration-tests/src/test/resources/oke/terraform/okeint/export_set.tf index b5572787e6a..8992e692eba 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/export_set.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/export_set.tf @@ -1,5 +1,5 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/file_system.tf b/integration-tests/src/test/resources/oke/terraform/okeint/file_system.tf index 2d01de2b4bf..76b6e5dff36 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/file_system.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/file_system.tf @@ -1,81 +1,81 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ resource "oci_file_storage_file_system" "oketest_fs1" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs2" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs3" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs4" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs5" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs6" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs7" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs8" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs9" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs10" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs11" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs12" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs13" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs14" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } resource "oci_file_storage_file_system" "oketest_fs15" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid } diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/kube_config.tf b/integration-tests/src/test/resources/oke/terraform/okeint/kube_config.tf index 546ee0a4834..432166102b5 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/kube_config.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/kube_config.tf @@ -1,5 +1,5 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/mount_target.tf b/integration-tests/src/test/resources/oke/terraform/okeint/mount_target.tf index 85fbc13e041..fd92ada8b48 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/mount_target.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/mount_target.tf @@ -1,13 +1,13 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ resource "oci_file_storage_mount_target" "oketest_mount_target" { #Required - availability_domain = data.oci_identity_availability_domain.ad1.name + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] compartment_id = var.compartment_ocid - subnet_id = var.private_subnet_ocid + subnet_id = oci_core_subnet.oke-subnet-worker-2.id #Optional display_name = "${var.cluster_name}-mt" diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/node-pool.tf b/integration-tests/src/test/resources/oke/terraform/okeint/node-pool.tf index d77ea5edb31..d19746492c5 100644 --- a/integration-tests/src/test/resources/oke/terraform/okeint/node-pool.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/node-pool.tf @@ -1,29 +1,7 @@ /* -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ - -variable "private_subnet_ocid" { -} - -variable "node_pool_node_config_details_node_pool_pod_network_option_details_cni_type" { - default = "OCI_VCN_IP_NATIVE" -} - -variable "node_pool_node_config_details_node_pool_pod_network_option_details_pod_nsg_ids" { - default = [] -} - -variable "node_pool_node_config_details_node_pool_pod_network_option_details_pod_subnet_ids" { - default = [] -} - -data "oci_identity_availability_domain" "ad1" { - compartment_id = var.tenancy_ocid - ad_number = 1 -} - - resource "oci_containerengine_node_pool" "tfsample_node_pool" { #Required cluster_id = oci_containerengine_cluster.tfsample_cluster.id @@ -31,33 +9,18 @@ resource "oci_containerengine_node_pool" "tfsample_node_pool" { kubernetes_version = var.node_pool_kubernetes_version name = var.node_pool_name node_shape = var.node_pool_node_shape - #subnet_ids = ["ocid1.subnet.oc1.phx.aaaaaaaah6tarxgsz2gcbaocbugpw5gjznbo7ylgtfb4yyonaezhbkm3a2xa"] + subnet_ids = [oci_core_subnet.oke-subnet-worker-1.id, oci_core_subnet.oke-subnet-worker-2.id] timeouts { create = "60m" delete = "2h" } - + node_eviction_node_pool_settings{ is_force_delete_after_grace_duration = true eviction_grace_duration = "PT0M" } - node_config_details { - #Required - placement_configs { - #Required - #availability_domain = "mFEn:PHX-AD-1" - availability_domain = data.oci_identity_availability_domain.ad1.name - subnet_id = var.private_subnet_ocid - } - size = 2 - } - node_shape_config { - #Optional - memory_in_gbs = 48.0 - ocpus = 4.0 - } # Using image Oracle-Linux-7.x- # Find image OCID for your region from https://docs.oracle.com/iaas/images/ @@ -66,6 +29,11 @@ resource "oci_containerengine_node_pool" "tfsample_node_pool" { source_type = "image" boot_volume_size_in_gbs = "200" } + node_shape_config { + #Optional + memory_in_gbs = 48.0 + ocpus = 4.0 + } # Optional initial_node_labels { key = "name" diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/provider.tf b/integration-tests/src/test/resources/oke/terraform/okeint/provider.tf index 29cb3e1b989..6bb6403a721 100755 --- a/integration-tests/src/test/resources/oke/terraform/okeint/provider.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/provider.tf @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. * This example file shows how to configure the oci provider to target the a single region. */ diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/vcn.tf b/integration-tests/src/test/resources/oke/terraform/okeint/vcn.tf new file mode 100755 index 00000000000..c605250e88d --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeint/vcn.tf @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. + * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +// Compartment in which to create the cluster resources. +variable "compartment_name" { +} + +variable "compartment_ocid" { +} + +variable "vcn_cidr_prefix" { + default = "10.0" +} + +variable "vcn_cidr" { + default = "10.0.0.0/16" +} + +# ------------------------------------ + +/* + * Create a VCN. + * A DNS label with the name of the cluster is attached to the VCN. + * The creation of the vcn also creates the default route table, security list, and dhcp options. + */ +resource "oci_core_virtual_network" "oke-vcn" { + cidr_block = var.vcn_cidr + dns_label = "${var.cluster_name}vcn" + compartment_id = var.compartment_ocid + display_name = "${var.cluster_name}_vcn" +} + +/* + * An internet gateway is created in the relevant compartment attached to the created VCN. + */ +resource "oci_core_internet_gateway" "oke-igateway" { + compartment_id = var.compartment_ocid + display_name = "${var.cluster_name}-igateway" + vcn_id = oci_core_virtual_network.oke-vcn.id +} + +/* + * Configures the default route table that was created when the VCN was created. + * The default route is pointed to the internet gateway that was created. + */ + +resource "oci_core_default_route_table" "oke-default-route-table" { + manage_default_resource_id = oci_core_virtual_network.oke-vcn.default_route_table_id + display_name = "${var.cluster_name}-default-route-table" + + route_rules { + cidr_block = "0.0.0.0/0" + network_entity_id = oci_core_internet_gateway.oke-igateway.id + } +} + +/* + * Configures the default dhcp options object that was created along with the VCN. + */ +resource "oci_core_default_dhcp_options" "oke-default-dhcp-options" { + manage_default_resource_id = oci_core_virtual_network.oke-vcn.default_dhcp_options_id + display_name = "${var.cluster_name}-default-dhcp-options" + + # required + options { + type = "DomainNameServer" + server_type = "VcnLocalPlusInternet" + } +} + +/* + * Configures the default security list. + */ +resource "oci_core_default_security_list" "oke-default-security-list" { + manage_default_resource_id = oci_core_virtual_network.oke-vcn.default_security_list_id + display_name = "${var.cluster_name}-default-security-list" + + // allow outbound tcp traffic on all ports + egress_security_rules { + destination = "0.0.0.0/0" + protocol = "all" + } + + // allow inbound ssh traffic + ingress_security_rules { + protocol = "6" // tcp + source = "0.0.0.0/0" + stateless = false + + tcp_options { + min = 22 + max = 22 + } + } + + // allow inbound icmp traffic of a specific type + ingress_security_rules { + protocol = 1 + source = "0.0.0.0/0" + + icmp_options { + type = 3 + code = 4 + } + } +} + +/* + * Security list for the worker subnets. + * - Stateless ingress/egress rule-pairs for the worker subnets. this lets traffic between the worker + * nodes flow freely. Stateless rule. + * - Contains a stateful rule to allow traffic to the internet - like for pulling docker images from + * DockerHub + * - Conatins two ingress rules to allow SSH traffic from OCI Cluster service. + */ +resource "oci_core_security_list" "oke-worker-security-list" { + compartment_id = var.compartment_ocid + display_name = "${var.cluster_name}-Workers-SecList" + vcn_id = oci_core_virtual_network.oke-vcn.id + + egress_security_rules { + destination = "0.0.0.0/0" + protocol = "6" // outbound TCP to the internet + stateless = false + } + egress_security_rules { + destination = "${var.vcn_cidr_prefix}.10.0/24" + protocol = "all" + stateless = true + } + egress_security_rules { + destination = "${var.vcn_cidr_prefix}.11.0/24" + protocol = "all" + stateless = true + } + egress_security_rules { + destination = "${var.vcn_cidr_prefix}.12.0/24" + protocol = "all" + stateless = true + } + egress_security_rules { + protocol = "6" // tcp + destination = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + tcp_options { + source_port_range { + min = 2048 + max = 2050 + } + } + } + egress_security_rules { + protocol = "6" // tcp + destination = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + tcp_options { + source_port_range { + min = 111 + max = 111 + } + } + } + egress_security_rules { + protocol = "17" // udp + destination = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + udp_options { + source_port_range { + min = 111 + max = 111 + } + } + } + + ingress_security_rules { + # Intra VCN traffic - this lets the 3 subnets in teh 3 ADs tak to each other without restriction. + # These are stateless, so they need to be accompanied by stateless egress rules. + stateless = true + + protocol = "all" + source = "${var.vcn_cidr_prefix}.10.0/24" + } + ingress_security_rules { + stateless = true + protocol = "all" + source = "${var.vcn_cidr_prefix}.11.0/24" + } + ingress_security_rules { + stateless = true + protocol = "all" + source = "${var.vcn_cidr_prefix}.12.0/24" + } + ingress_security_rules { + # ICMP + protocol = 1 + source = "0.0.0.0/0" + + icmp_options { + type = 3 + code = 4 + } + } + ingress_security_rules { + # OCI Cluster service + protocol = "6" // tcp + source = "130.35.0.0/16" + stateless = false + + tcp_options { + min = 22 + max = 22 + } + } + ingress_security_rules { + protocol = "6" // tcp + source = "138.1.0.0/17" + stateless = false + + tcp_options { + min = 22 + max = 22 + } + } + ingress_security_rules { + # NodePort ingress rules + protocol = "6" // tcp + source = "0.0.0.0/0" + stateless = true + + tcp_options { + min = 30000 + max = 32767 + } + } + ingress_security_rules { + protocol = "6" // tcp + source = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + tcp_options { + source_port_range { + min = 2048 + max = 2050 + } + } + } + ingress_security_rules { + protocol = "6" // tcp + source = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + tcp_options { + source_port_range { + min = 111 + max = 111 + } + } + } + ingress_security_rules { + protocol = "17" // udp + source = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + udp_options { + source_port_range { + min = 111 + max = 111 + } + } + } + ingress_security_rules { + protocol = "17" // udp + source = "${var.vcn_cidr_prefix}.0.0/16" + stateless = true + + udp_options { + source_port_range { + min = 2048 + max = 2048 + } + } + } + ingress_security_rules { + # SSH Stateful ingress rules + protocol = "6" // tcp + source = "0.0.0.0/0" + stateless = false + + tcp_options { + min = 22 + max = 22 + } + } +} + +/* + * Security list for the loadbalancer subnets. + * - Allows all TCP traffic in/out. + */ +resource "oci_core_security_list" "oke-lb-security-list" { + compartment_id = var.compartment_ocid + display_name = "${var.cluster_name}-LoadBalancers-SecList" + vcn_id = oci_core_virtual_network.oke-vcn.id + + egress_security_rules { + destination = "0.0.0.0/0" + protocol = "6" + stateless = true + } + + ingress_security_rules { + protocol = "6" + source = "0.0.0.0/0" + stateless = true + } +} + +/* + * Create the subnets. + * A total of 5 Subnets are created. This is just a basic config. + * + * Worker Subnets + * -------------- + * 3 Subnets are for worker nodes in the node pool. The workers are spreead across 3 availability + * domains, and one subnet is created for each AD to host workers in that AD. + * Obviously worker is a generic term, and assumes that the workload is homogeneous. + * For more realistic topologies, you may need to create additional subnets and security rules to say, + * separate parts of the application or certains components like a DB in to a separate subnet + * with separate security lists. You can for example create subnets to host frontend pods, + * middle tier pods as well as data store pods. You may want to restrict front ends to just have + * access to middle tier, but not DBs. + * + * LB subnets + * ---------- + * These host the LoadBalancers. If the K8s deployment create a service of type Loadbalancer then an + * OCI loadbalancer is provisioned and this is placed in this subnet. The two subnet exists, because + * OCI loadbalancers can provide a flaoting VIP that can move over to the second availability domain + * in case the first one fails for some reason. Typical HA config. + * + */ + +resource "oci_core_subnet" "oke-subnet-worker-1" { + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[0]["name"] + cidr_block = "${var.vcn_cidr_prefix}.10.0/24" + display_name = "${var.cluster_name}-WorkerSubnet01" + dns_label = "workers01" + compartment_id = var.compartment_ocid + vcn_id = oci_core_virtual_network.oke-vcn.id + security_list_ids = [oci_core_security_list.oke-worker-security-list.id] + route_table_id = oci_core_virtual_network.oke-vcn.default_route_table_id + dhcp_options_id = oci_core_virtual_network.oke-vcn.default_dhcp_options_id +} + +resource "oci_core_subnet" "oke-subnet-worker-2" { + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] + cidr_block = "${var.vcn_cidr_prefix}.11.0/24" + display_name = "${var.cluster_name}-WorkerSubnet02" + dns_label = "workers02" + compartment_id = var.compartment_ocid + vcn_id = oci_core_virtual_network.oke-vcn.id + security_list_ids = [oci_core_security_list.oke-worker-security-list.id] + route_table_id = oci_core_virtual_network.oke-vcn.default_route_table_id + dhcp_options_id = oci_core_virtual_network.oke-vcn.default_dhcp_options_id +} + +resource "oci_core_subnet" "oke-subnet-worker-3" { + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[2]["name"] + cidr_block = "${var.vcn_cidr_prefix}.12.0/24" + display_name = "${var.cluster_name}-WorkerSubnet03" + dns_label = "workers03" + compartment_id = var.compartment_ocid + vcn_id = oci_core_virtual_network.oke-vcn.id + security_list_ids = [oci_core_security_list.oke-worker-security-list.id] + route_table_id = oci_core_virtual_network.oke-vcn.default_route_table_id + dhcp_options_id = oci_core_virtual_network.oke-vcn.default_dhcp_options_id +} + +resource "oci_core_subnet" "oke-subnet-loadbalancer-1" { + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[0]["name"] + cidr_block = "${var.vcn_cidr_prefix}.20.0/24" + display_name = "${var.cluster_name}-LB-Subnet01" + dns_label = "lb01" + compartment_id = var.compartment_ocid + vcn_id = oci_core_virtual_network.oke-vcn.id + security_list_ids = [oci_core_security_list.oke-lb-security-list.id] + route_table_id = oci_core_virtual_network.oke-vcn.default_route_table_id + dhcp_options_id = oci_core_virtual_network.oke-vcn.default_dhcp_options_id +} + +resource "oci_core_subnet" "oke-subnet-loadbalancer-2" { + availability_domain = data.oci_identity_availability_domains.ADs.availability_domains[1]["name"] + cidr_block = "${var.vcn_cidr_prefix}.21.0/24" + display_name = "${var.cluster_name}-LB-Subnet02" + dns_label = "lb02" + compartment_id = var.compartment_ocid + vcn_id = oci_core_virtual_network.oke-vcn.id + security_list_ids = [oci_core_security_list.oke-lb-security-list.id] + route_table_id = oci_core_virtual_network.oke-vcn.default_route_table_id + dhcp_options_id = oci_core_virtual_network.oke-vcn.default_dhcp_options_id +} + +/** + * Get the avaialbility domains for this tennancy. + * Using any compartment id in this tennancy should also work just as well. + */ +data "oci_identity_availability_domains" "ADs" { + compartment_id = var.tenancy_ocid +} + +/* + * Query the compartment we created (or re-used) + */ +data "oci_identity_compartments" "oke-compartment" { + compartment_id = var.compartment_ocid + + filter { + name = "name" + values = [var.compartment_name] + } +} + +data "oci_core_virtual_networks" "oke-vcns" { + #Required + compartment_id = oci_core_virtual_network.oke-vcn.compartment_id + + #Filter + display_name = oci_core_virtual_network.oke-vcn.display_name +} + +data "oci_core_internet_gateways" "oke-igateways" { + #Required + compartment_id = oci_core_internet_gateway.oke-igateway.compartment_id + vcn_id = oci_core_internet_gateway.oke-igateway.vcn_id +} + +data "oci_core_route_tables" "oke_route_tables" { + #Required + compartment_id = oci_core_virtual_network.oke-vcn.compartment_id + vcn_id = oci_core_virtual_network.oke-vcn.id +} + +data "oci_core_dhcp_options" "oke_dhcp_options" { + #Required + compartment_id = oci_core_virtual_network.oke-vcn.compartment_id + vcn_id = oci_core_virtual_network.oke-vcn.id +} + +data "oci_core_security_lists" "oke_security_lists" { + #Required + compartment_id = oci_core_security_list.oke-worker-security-list.compartment_id + vcn_id = oci_core_security_list.oke-worker-security-list.vcn_id +} + +data "oci_core_subnets" "oke_subnets" { + #Required + compartment_id = oci_core_subnet.oke-subnet-worker-1.compartment_id + vcn_id = oci_core_subnet.oke-subnet-worker-1.vcn_id +} + +# Print out the VCN objects that were created. + +output "Compartments" { + value = data.oci_identity_compartments.oke-compartment.compartments +} + +output "VCN" { + value = data.oci_core_virtual_networks.oke-vcns.virtual_networks +} + +output "InternetGateway" { + value = data.oci_core_internet_gateways.oke-igateways.gateways +} + +output "RouteTables" { + value = data.oci_core_route_tables.oke_route_tables.route_tables +} + +output "DHCPOptions" { + value = data.oci_core_dhcp_options.oke_dhcp_options.options +} + +output "SecurityLists" { + value = data.oci_core_security_lists.oke_security_lists.security_lists +} + +output "Subnets" { + value = data.oci_core_subnets.oke_subnets.subnets +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeint/versions.tf b/integration-tests/src/test/resources/oke/terraform/okeint/versions.tf index 653e064c63d..bdcdcc3861c 100644 --- a/integration-tests/src/test/resources/oke/terraform/okeint/versions.tf +++ b/integration-tests/src/test/resources/oke/terraform/okeint/versions.tf @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. + * Copyright (c) 2021, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/cluster.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/cluster.tf new file mode 100755 index 00000000000..075d8e7de81 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/cluster.tf @@ -0,0 +1,117 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +// Compartment in which to create the cluster resources. +variable "compartment_name" { +} + +variable "compartment_ocid" { +} + +variable "sub_compartment_ocid" { +} + +variable "cluster_kubernetes_version" { + default = "v1.17.9" +} + +variable "cluster_name" { + default = "tfTestCluster" +} + +variable "cluster_options_add_ons_is_kubernetes_dashboard_enabled" { + default = true +} + +variable "cluster_options_add_ons_is_tiller_enabled" { + default = true +} + +variable "cluster_options_kubernetes_network_config_pods_cidr" { + default = "10.1.0.0/16" +} + +variable "cluster_options_kubernetes_network_config_services_cidr" { + default = "10.2.0.0/16" +} + +variable "node_pool_initial_node_labels_key" { + default = "key" +} + +variable "node_pool_initial_node_labels_value" { + default = "value" +} + +variable "node_pool_kubernetes_version" { + default = "v1.17.9" +} + +variable "node_pool_name" { + default = "tfTestCluster_workers" +} + +variable "node_pool_node_image_name" { + default = "Oracle-Linux-7.6" +} + +variable "node_pool_node_shape" { + default = "VM.Standard2.1" +} + +variable "node_pool_quantity_per_subnet" { + default = 1 +} + +variable "node_pool_ssh_public_key" { +} + +variable "vcn_ocid" { +} +variable "mount_target_ocid" { +} + +variable "pub_subnet_ocid" { +} + +variable "cluster_cluster_pod_network_options_cni_type" { + default = "OCI_VCN_IP_NATIVE" +} + +data "oci_identity_availability_domains" "tfsample_availability_domains" { + compartment_id = var.sub_compartment_ocid +} + +resource "oci_containerengine_cluster" "tfsample_cluster" { + #Required + compartment_id = var.compartment_ocid + kubernetes_version = var.cluster_kubernetes_version + name = var.cluster_name + vcn_id = var.vcn_ocid + + endpoint_config { + + #Optional + is_public_ip_enabled = "true" + subnet_id = var.pub_subnet_ocid + } + #Optional + options { + service_lb_subnet_ids = [var.pub_subnet_ocid] + + #Optional + add_ons { + #Optional + is_kubernetes_dashboard_enabled = var.cluster_options_add_ons_is_kubernetes_dashboard_enabled + is_tiller_enabled = var.cluster_options_add_ons_is_tiller_enabled + } + } +} + + +output "cluster_id" { + value = oci_containerengine_cluster.tfsample_cluster.id +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/export.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/export.tf new file mode 100755 index 00000000000..33e2c87880e --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/export.tf @@ -0,0 +1,17 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ +resource "oci_file_storage_export" "oketest_export1" { + #Required + export_set_id = oci_file_storage_export_set.oketest_export_set.id + file_system_id = oci_file_storage_file_system.oketest_fs1.id + path = "/${var.cluster_name}oketest1" +} +resource "oci_file_storage_export" "oketest_export2" { + #Required + export_set_id = oci_file_storage_export_set.oketest_export_set.id + file_system_id = oci_file_storage_file_system.oketest_fs2.id + path = "/${var.cluster_name}oketest2" +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/export_set.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/export_set.tf new file mode 100755 index 00000000000..8c9c5639977 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/export_set.tf @@ -0,0 +1,10 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +resource "oci_file_storage_export_set" "oketest_export_set" { + # Required + mount_target_id = var.mount_target_ocid +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/file_system.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/file_system.tf new file mode 100755 index 00000000000..00277f42aa8 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/file_system.tf @@ -0,0 +1,15 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +resource "oci_file_storage_file_system" "oketest_fs1" { + #Required + availability_domain = data.oci_identity_availability_domain.ad1.name + compartment_id = var.compartment_ocid +} +resource "oci_file_storage_file_system" "oketest_fs2" { + #Required + availability_domain = data.oci_identity_availability_domain.ad1.name + compartment_id = var.compartment_ocid +} diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/kube_config.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/kube_config.tf new file mode 100755 index 00000000000..c8e74a60887 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/kube_config.tf @@ -0,0 +1,23 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +variable "cluster_kube_config_expiration" { + default = 2592000 +} + +variable "cluster_kube_config_token_version" { + default = "2.0.0" +} + +data "oci_containerengine_cluster_kube_config" "tfsample_cluster_kube_config" { + #Required + cluster_id = oci_containerengine_cluster.tfsample_cluster.id +} + +resource "local_file" "tfsample_cluster_kube_config_file" { + content = data.oci_containerengine_cluster_kube_config.tfsample_cluster_kube_config.content + filename = "${path.module}/${var.cluster_name}_kubeconfig" +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/node-pool.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/node-pool.tf new file mode 100644 index 00000000000..185199d16c1 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/node-pool.tf @@ -0,0 +1,76 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +variable "private_subnet_ocid" { +} + +variable "node_pool_node_config_details_node_pool_pod_network_option_details_cni_type" { + default = "OCI_VCN_IP_NATIVE" +} + +variable "node_pool_node_config_details_node_pool_pod_network_option_details_pod_nsg_ids" { + default = [] +} + +variable "node_pool_node_config_details_node_pool_pod_network_option_details_pod_subnet_ids" { + default = [] +} + +data "oci_identity_availability_domain" "ad1" { + compartment_id = var.tenancy_ocid + ad_number = 1 +} + + +resource "oci_containerengine_node_pool" "tfsample_node_pool" { + #Required + cluster_id = oci_containerengine_cluster.tfsample_cluster.id + compartment_id = var.compartment_ocid + kubernetes_version = var.node_pool_kubernetes_version + name = var.node_pool_name + node_shape = var.node_pool_node_shape + #subnet_ids = ["ocid1.subnet.oc1.phx.aaaaaaaah6tarxgsz2gcbaocbugpw5gjznbo7ylgtfb4yyonaezhbkm3a2xa"] + + timeouts { + create = "60m" + delete = "2h" + } + + node_eviction_node_pool_settings{ + is_force_delete_after_grace_duration = true + eviction_grace_duration = "PT0M" + } + node_config_details { + #Required + placement_configs { + #Required + #availability_domain = "mFEn:PHX-AD-1" + availability_domain = data.oci_identity_availability_domain.ad1.name + subnet_id = var.private_subnet_ocid + + } + size = 2 + } + node_shape_config { + #Optional + memory_in_gbs = 48.0 + ocpus = 4.0 + } + + # Using image Oracle-Linux-7.x- + # Find image OCID for your region from https://docs.oracle.com/iaas/images/ + node_source_details { + image_id = var.node_pool_node_image_name + source_type = "image" + boot_volume_size_in_gbs = "200" + } + # Optional + initial_node_labels { + key = "name" + value = "var.cluster_name" + } + ssh_public_key = var.node_pool_ssh_public_key +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okeintdev/oke.create.sh new file mode 100755 index 00000000000..d971b076890 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/oke.create.sh @@ -0,0 +1,195 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +prop() { + grep "${1}" ${propsFile}| grep -v "#" | cut -d'=' -f2 +} + + +generateTFVarFile() { + tfVarsFiletfVarsFile=${terraformVarDir}/${clusterTFVarsFile}.tfvars + rm -f ${tfVarsFiletfVarsFile} + cp ${terraformVarDir}/template.tfvars $tfVarsFiletfVarsFile + chmod 777 ${terraformVarDir}/template.tfvars $tfVarsFiletfVarsFile + sed -i -e "s:@TENANCYOCID@:${tenancy_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@USEROCID@:${user_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@COMPOCID@:${compartment_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@SUBCOMPARTMENTOCID@:${sub_compartment_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@COMPARTMENTNAME@:${compartment_name}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@OKECLUSTERNAME@:${okeclustername}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s/@OCIAPIPUBKEYFINGERPRINT@/"${ociapi_pubkey_fingerprint}"/g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@OCIPRIVATEKEYPATH@:${ocipk_path}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@VCNCIDRPREFIX@:${vcn_cidr_prefix}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@VCNOCID@:${vcn_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@PUBSUBNETOCID@:${pub_subnet_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@PRIVATESUBNETOCID@:${private_subnet_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@VCNCIDR@:${vcn_cidr_prefix}.0.0/16:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@OKEK8SVERSION@:${k8s_version}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLSHAPE@:${nodepool_shape}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLIMAGENAME@:${nodepool_imagename}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLSSHPUBKEY@:${nodepool_ssh_pubkey}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@REGION@:${region}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@MOUNTTARGETOCID@:${mount_target_ocid}:g" ${tfVarsFiletfVarsFile} + echo "Generated TFVars file [${tfVarsFiletfVarsFile}]" +} + +setupTerraform() { + mkdir ${terraformDir} + cd ${terraformDir} + if [[ "${OSTYPE}" == "darwin"* ]]; then + os_type="darwin" + elif [[ "${OSTYPE}" == "linux"* ]]; then + os_type="linux" + else + echo "Unsupported OS" + fi + curl -O https://releases.hashicorp.com/terraform/1.8.1/terraform_1.8.1_${os_type}_${platform}64.zip + unzip terraform_1.8.1_${os_type}_${platform}64.zip + chmod +x ${terraformDir}/terraform + export PATH=${terraformDir}:${PATH} +} + +deleteOlderVersionTerraformOCIProvider() { + if [ -d ~/.terraform.d/plugins ]; then + echo "Deleting older version of terraform plugins dir" + rm -rf ~/.terraform.d/plugins + fi + if [ -d ${terraformVarDir}/.terraform ]; then + rm -rf ${terraformVarDir}/.terraform + fi + if [ -e ~/.terraformrc ]; then + rm ~/.terraformrc + fi +} + +createCluster () { + cd ${terraformVarDir} + echo "terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + terraform plan -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + terraform apply -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars +} + +createRoleBindings () { + ${KUBERNETES_CLI:-kubectl} -n kube-system create serviceaccount $okeclustername-sa + ${KUBERNETES_CLI:-kubectl} create clusterrolebinding add-on-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:$okeclustername-sa + TOKENNAME=`${KUBERNETES_CLI:-kubectl} -n kube-system get serviceaccount/$okeclustername-sa -o jsonpath='{.secrets[0].name}'` + TOKEN=`${KUBERNETES_CLI:-kubectl} -n kube-system get secret $TOKENNAME -o jsonpath='{.data.token}'| base64 --decode` + ${KUBERNETES_CLI:-kubectl} config set-credentials $okeclustername-sa --token=$TOKEN + ${KUBERNETES_CLI:-kubectl} config set-context --current --user=$okeclustername-sa +} + +checkClusterRunning () { + + echo "Confirm we have ${KUBERNETES_CLI:-kubectl} working..." + myline=`${KUBERNETES_CLI:-kubectl} get nodes | awk '{print $2}'| tail -n+2` + status="NotReady" + max=50 + count=1 + + privateIP=${vcn_cidr_prefix//./\\.}\\.10\\. + privateIP=${vcn_cidr_prefix} + declare -a myline + myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) + NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` + status=$myline[0] + max=100 + count=1 + + for i in {0..1} + do + while [ "${myline[i]}" != "Ready" -a $count -le $max ] ; do + echo "echo '[ERROR] Some Nodes in the Cluster are not in the Ready Status , sleep 10s more ..." + sleep 10 + myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) + NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` + echo "myline[i] ${myline[i]}" + [[ ${myline[i]} -eq "Ready" ]] + echo "Status is ${myline[i]} Iter [$count/$max]" + count=`expr $count + 1` + done + done + + NODES=`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | wc -l` + if [ "$NODES" == "2" ]; then + echo '- looks good' + else + echo '- could not talk to cluster, aborting' + cd ${terraformVarDir} + terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + exit 1 + fi + + if [ $count -gt $max ] ; then + echo "[ERROR] Unable to start the nodes in oke cluster after 200s "; + cd ${terraformVarDir} + terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + exit 1 + fi +} + +#MAIN +propsFile=${1:-$PWD/oci.props} +terraformVarDir=${2:-$PWD} +platform=${3:-amd} + +#grep props's values from oci.props file + +clusterTFVarsFile=$(prop 'tfvars.filename') +tenancy_ocid=$(prop 'tenancy.ocid') +user_ocid=$(prop 'user.ocid') +mount_target_ocid=$(prop 'mounttarget.ocid') +compartment_ocid=$(prop 'compartment.ocid') +sub_compartment_ocid=$(prop 'sub.comp.ocid') +compartment_name=$(prop 'compartment.name') +okeclustername=$(prop 'okeclustername') +ociapi_pubkey_fingerprint=$(prop 'ociapi.pubkey.fingerprint') +ocipk_path=$(prop 'ocipk.path') +vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') +vcn_ocid=$(prop 'vcn.ocid') +pub_subnet_ocid=$(prop 'pub.subnet.ocid') +private_subnet_ocid=$(prop 'private.subnet.ocid') +k8s_version=$(prop 'k8s.version') +nodepool_shape=$(prop 'nodepool.shape') +nodepool_imagename=$(prop 'nodepool.imagename') +nodepool_ssh_pubkey=$(prop 'nodepool.ssh.pubkey') +region=$(prop 'region') +terraformDir=$(prop 'terraform.installdir') + +# generate terraform configuration file with name $(clusterTFVarsFile).tfvar +#generateTFVarFile +generateTFVarFile + +# cleanup previously installed terraform binaries +rm -rf ${terraformDir} + +# download terraform binaries into ${terraformDir} +setupTerraform + +# clean previous versions of terraform oci provider +deleteOlderVersionTerraformOCIProvider + +chmod 600 ${ocipk_path} + +# run terraform init,plan,apply to create OKE cluster based on the provided tfvar file ${clusterTFVarsFile).tfvar +createCluster +#check status of OKE cluster nodes, destroy if can not access them +export KUBECONFIG=${terraformVarDir}/${okeclustername}_kubeconfig + + +export okeclustername=\"${okeclustername}\" + + + echo " oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"'" + +clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') +echo "clusterIp : $clusterIP" +clusterPublicIP=${clusterIP:1:-6} +echo " clusterPublicIP : ${clusterPublicIP}" +export NO_PROXY=$NO_PROXY,${clusterPublicIP} +echo "NO_PROXY:" $NO_PROXY + + +checkClusterRunning +echo "$okeclustername is up and running}" diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/oke.delete.sh b/integration-tests/src/test/resources/oke/terraform/okeintdev/oke.delete.sh new file mode 100644 index 00000000000..9648d87b6a8 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/oke.delete.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# +# This script deletes provisioned OKE Kubernetes cluster using terraform (https://www.terraform.io/) +# + +set -o errexit +set -o pipefail + +prop() { + grep "${1}" ${oci_property_file}| grep -v "#" | cut -d'=' -f2 +} + +cleanupLB() { + echo 'Clean up left over LB' + myvcn_id=`oci network vcn list --compartment-id $compartment_ocid --display-name=${clusterName}_vcn | jq -r '.data[] | .id'` + declare -a vcnidarray + vcnidarray=(${myvcn_id// /}) + myip=`oci lb load-balancer list --compartment-id $compartment_ocid |jq -r '.data[] | .id'` + mysubnets=`oci network subnet list --vcn-id=${vcnidarray[0]} --display-name=${clusterName}-LB-${1} --compartment-id $compartment_ocid | jq -r '.data[] | .id'` + + declare -a iparray + declare -a mysubnetsidarray + mysubnetsidarray=(${mysubnets// /}) + + iparray=(${myip// /}) + vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') + for k in "${mysubnetsidarray[@]}" + do + for i in "${iparray[@]}" + do + lb=`oci lb load-balancer get --load-balancer-id=$i` + echo "deleting lb with id $i $lb" + if [[ (-z "${lb##*$vcn_cidr_prefix*}") || (-z "${lb##*$k*}") ]] ;then + echo "deleting lb with id $i" + sleep 60 + oci lb load-balancer delete --load-balancer-id=$i --force || true + fi + done + done + myip=`oci lb load-balancer list --compartment-id $compartment_ocid |jq -r '.data[] | .id'` + iparray=(${myip// /}) + for k in "${mysubnetsidarray[@]}" + do + for i in "${iparray[@]}" + do + lb=`oci lb load-balancer get --load-balancer-id=$i` + echo "deleting lb with id $i $lb" + if [[ (-z "${lb##*$vcn_cidr_prefix*}") || (-z "${lb##*$k*}") ]] ;then + echo "deleting lb with id $i" + sleep 60 + oci lb load-balancer delete --load-balancer-id=$i --force || true + fi + done + done +} + +deleteOKE() { + cd ${terraform_script_dir} + terraform init -var-file=${terraform_script_dir}/${clusterName}.tfvars + terraform plan -var-file=${terraform_script_dir}/${clusterName}.tfvars + terraform destroy -auto-approve -var-file=${terraform_script_dir}/${clusterName}.tfvars +} + + +#MAIN +oci_property_file=${1:-$PWD/oci.props} +terraform_script_dir=${2:-$PWD} +availability_domain=${3:-mFEn:PHX-AD-1} +clusterName=$(prop 'okeclustername') +compartment_ocid=$(prop 'compartment.ocid') +vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') +export KUBECONFIG=${terraform_script_dir}/${clusterName}_kubeconfig +export PATH=${terraform_script_dir}/terraforminstall:$PATH +echo 'Deleting cluster' +#check and cleanup any left over running Load Balancers +out=$(cleanupLB Subnet01 && :) +echo $out +out=$(cleanupLB Subnet02 && :) +echo $out +deleteOKE || true diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/provider.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/provider.tf new file mode 100755 index 00000000000..903007a13be --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/provider.tf @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + * This example file shows how to configure the oci provider to target the a single region. +*/ + +// These variables would commonly be defined as environment variables or sourced in a .env file +variable "tenancy_ocid" { +} + +variable "user_ocid" { +} + +variable "fingerprint" { +} + +variable "private_key_path" { +} + +variable "region" { + default = "us-phoenix-1" +} + +terraform { + required_providers { + oci = { + source = "oracle/oci" + #version = "4.87.0" + } + } +} + +provider "oci" { + region = var.region + tenancy_ocid = var.tenancy_ocid + user_ocid = var.user_ocid + fingerprint = var.fingerprint + private_key_path = var.private_key_path +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/template.tfvars b/integration-tests/src/test/resources/oke/terraform/okeintdev/template.tfvars new file mode 100755 index 00000000000..6716c1c7195 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/template.tfvars @@ -0,0 +1,53 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Template to generate TF variables file for cluster creation from property file oci.props +# +# User-specific vars - you can get these easily from the OCI console from your user page +# + +# OCID can be obtained from the user info page in the OCI console +user_ocid="@USEROCID@" +# API key fingerprint and private key location, needed for API access -- you should have added a public API key through the OCI console first +fingerprint="@OCIAPIPUBKEYFINGERPRINT@" +private_key_path="@OCIPRIVATEKEYPATH@" + +# Required tenancy vars +tenancy_ocid="@TENANCYOCID@" +compartment_ocid="@COMPOCID@" +compartment_name="@COMPARTMENTNAME@" +sub_compartment_ocid="@SUBCOMPARTMENTOCID@" +region="@REGION@" + +# +# Cluster-specific vars +# + +# VCN CIDR -- must be unique within the compartment in the tenancy +# - assuming 1:1 cluster:vcn +# BE SURE TO SET BOTH VARS -- the first 2 octets for each variable have to match +vcn_cidr_prefix="@VCNCIDRPREFIX@" +vcn_cidr="@VCNCIDR@" +vcn_ocid="@VCNOCID@" + +# Subnet + +pub_subnet_ocid="@PUBSUBNETOCID@" +private_subnet_ocid="@PRIVATESUBNETOCID@" +# Cluster name and k8s version +cluster_kubernetes_version="@OKEK8SVERSION@" +cluster_name="@OKECLUSTERNAME@" + +# Node pool info +node_pool_kubernetes_version="@OKEK8SVERSION@" +node_pool_name="@OKECLUSTERNAME@_workers" +node_pool_node_shape="@NODEPOOLSHAPE@" +node_pool_node_image_name="@NODEPOOLIMAGENAME@" +node_pool_quantity_per_subnet=1 + +# SSH public key, for SSH access to nodes in the cluster +node_pool_ssh_public_key="@NODEPOOLSSHPUBKEY@" + +#MountTarget +mount_target_ocid="@MOUNTTARGETOCID@" + diff --git a/integration-tests/src/test/resources/oke/terraform/okeintdev/versions.tf b/integration-tests/src/test/resources/oke/terraform/okeintdev/versions.tf new file mode 100644 index 00000000000..f74ee99a3f5 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okeintdev/versions.tf @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +terraform { + required_version = ">= 0.12" +} From 5cdf633f841b911ae452144dbe4ed4b48a2e344d Mon Sep 17 00:00:00 2001 From: Sankar Neelakandan Date: Thu, 25 Apr 2024 10:31:00 -0700 Subject: [PATCH 045/356] start nightly podman job for release/4.2 --- Jenkinsfile.podman | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index cba5d194e3a..3ef209c8249 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -20,9 +20,7 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential - H 3 * * * % MAVEN_PROFILE_NAME=wls-srg''' +CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel''' pipeline { agent { label 'large-ol9' } @@ -32,7 +30,7 @@ pipeline { triggers { // timer trigger for "nightly build" - parameterizedCron(env.JOB_NAME == 'wko-kind-main-nightly-podman' ? + parameterizedCron(env.JOB_NAME == 'wko-kind-release42-nightly-podman' ? CRON_SETTINGS : '') } @@ -64,7 +62,7 @@ pipeline { parameters { string(name: 'BRANCH', description: 'The branch to run the tests on', - defaultValue: 'main' + defaultValue: 'release/4.2' ) choice(name: 'MAVEN_PROFILE_NAME', @@ -92,13 +90,13 @@ pipeline { description: 'Kubernetes version. Supported values depend on the Kind version. Kind 0.22.0: 1.29, 1.29.2, 1.28, 1.28.7, 1.27, 1.27.11, 1.26, 1.26.14, 1.25, 1.25.16, 1.24, 1.24.17, 1.23, 1.23.17 ', choices: [ // The first item in the list is the default value... - '1.26.14', '1.29.2', '1.29', '1.28.7', '1.28', '1.27.11', '1.27', + '1.26.14', '1.26', '1.25.16', '1.25', @@ -605,9 +603,7 @@ EOF stage ('Sync') { when { anyOf { - branch 'main' - branch 'release/4.0' - branch 'release/3.4' + branch 'release/4.2' } anyOf { not { triggeredBy 'TimerTrigger' } From 15d11b5945b8474285241a95148ecafaf7a7a7ae Mon Sep 17 00:00:00 2001 From: xian_cao Date: Thu, 25 Apr 2024 18:35:14 +0000 Subject: [PATCH 046/356] [backport] [podman] backport MR 4639, 4643 and 4645 to release/4.2 --- .../kubernetes/ItWlsDomainOnPVSample.java | 32 ++++++++++-- .../kubernetes/ItWlsMiiLegacySample.java | 32 ++++++++++-- .../weblogic/kubernetes/ItWlsMiiSample.java | 31 +++++++++-- .../domain-on-pv/test-env.sh | 6 ++- .../domain-on-pv/util-misc.sh | 6 ++- .../model-in-image/run-test.sh | 3 -- .../model-in-image/test-env.sh | 6 ++- .../model-in-image/util-misc.sh | 51 ++++++------------- validateCLI.docker.dat | 3 +- 9 files changed, 116 insertions(+), 54 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPVSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPVSample.java index 2834c284df6..fbec2d31420 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPVSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPVSample.java @@ -1,8 +1,9 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; +import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,12 +38,16 @@ import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_REGISTRY; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_NAMESPACE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_JAVA_HOME; @@ -56,6 +61,7 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.SampleUtils.createPVHostPathAndChangePermissionInKindCluster; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -71,13 +77,13 @@ class ItWlsDomainOnPVSample { private static final String domainOnPvSampleScript = "../operator/integration-tests/domain-on-pv/run-test.sh"; - private static final String DOMAIN_CREATION_IMAGE_NAME = "wdt-domain-image"; private static final String DOMAIN_CREATION_IMAGE_WLS_TAG = "WLS-v1"; private static String traefikNamespace = null; private static Map envMap = null; private static LoggingFacade logger = null; private boolean previousTestSuccessful = true; + private static String DOMAIN_CREATION_IMAGE_NAME = "wdt-domain-image"; /** * Create namespaces and set environment variables for the test. @@ -122,7 +128,8 @@ public static void initAll(@Namespaces(3) List namespaces) { envMap.put("BASE_IMAGE_TAG", WEBLOGIC_IMAGE_TAG); envMap.put("IMAGE_PULL_SECRET_NAME", BASE_IMAGES_REPO_SECRET_NAME); envMap.put("DOMAIN_IMAGE_PULL_SECRET_NAME", TEST_IMAGES_REPO_SECRET_NAME); - envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); + envMap.put("WLSIMG_BUILDER_DEFAULT", WLSIMG_BUILDER_DEFAULT); + envMap.put("WLSIMG_BUILDER", WLSIMG_BUILDER); envMap.put("OKD", "" + OKD); envMap.put("KIND_CLUSTER", "" + KIND_CLUSTER); envMap.put("OCNE", "" + OCNE); @@ -144,6 +151,18 @@ public static void initAll(@Namespaces(3) List namespaces) { if (WDT_DOWNLOAD_URL != null) { envMap.put("WDT_INSTALLER_URL", WDT_DOWNLOAD_URL); } + + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + DOMAIN_CREATION_IMAGE_NAME = "localhost/wdt-domain-image"; + envMap.put("OPER_IMAGE_NAME", "localhost/weblogic-kubernetes-operator"); + envMap.put("DOMAIN_CREATION_IMAGE_NAME", DOMAIN_CREATION_IMAGE_NAME); + envMap.put("K8S_NODEPORT_HOST", assertDoesNotThrow(() -> InetAddress.getLocalHost().getHostAddress())); + envMap.put("TRAEFIK_INGRESS_HTTP_HOSTPORT", "" + TRAEFIK_INGRESS_HTTP_HOSTPORT); + envMap.put("TRAEFIK_NAMESPACE", TRAEFIK_NAMESPACE); + } else { + envMap.put("TRAEFIK_NAMESPACE", traefikNamespace); + envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); + } logger.info("Environment variables to the script {0}", envMap); logger.info("Setting up image registry secrets"); @@ -176,7 +195,12 @@ public void testInstallOperator() { @Test @Order(2) public void testInstallTraefik() { - execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + logger.info("skip installing Traefik in KIND and podman environment"); + logger.info("Traefik is already installed in InitialTask in namespace %s", TRAEFIK_NAMESPACE); + } else { + execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + } } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiLegacySample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiLegacySample.java index 52b0f4bd215..a2260ea8056 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiLegacySample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiLegacySample.java @@ -3,6 +3,7 @@ package oracle.weblogic.kubernetes; +import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,12 +34,16 @@ import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_REGISTRY; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_NAMESPACE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_JAVA_HOME; @@ -49,6 +54,7 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -64,7 +70,6 @@ class ItWlsMiiLegacySample { private static final String miiSampleScript = "../operator/integration-tests/model-in-image/run-test.sh"; - private static final String DOMAIN_CREATION_IMAGE_NAME = "wdt-domain-image"; private static final String DOMAIN_CREATION_IMAGE_WLS_TAG = "WLS-LEGACY-v1"; private static final String MODEL_IMAGE_WLS_TAG = "WLS-LEGACY-v2"; private static String traefikNamespace = null; @@ -72,6 +77,7 @@ class ItWlsMiiLegacySample { private static LoggingFacade logger = null; private boolean previousTestSuccessful = true; + private static String DOMAIN_CREATION_IMAGE_NAME = "wdt-domain-image"; /** * Create namespaces and set environment variables for the test. @@ -117,7 +123,8 @@ public static void initAll(@Namespaces(3) List namespaces) { envMap.put("BASE_IMAGE_TAG", WEBLOGIC_IMAGE_TAG); envMap.put("IMAGE_PULL_SECRET_NAME", BASE_IMAGES_REPO_SECRET_NAME); envMap.put("DOMAIN_IMAGE_PULL_SECRET_NAME", TEST_IMAGES_REPO_SECRET_NAME); - envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); + envMap.put("WLSIMG_BUILDER_DEFAULT", WLSIMG_BUILDER_DEFAULT); + envMap.put("WLSIMG_BUILDER", WLSIMG_BUILDER); envMap.put("OKD", "" + OKD); envMap.put("KIND_CLUSTER", "" + KIND_CLUSTER); @@ -133,6 +140,19 @@ public static void initAll(@Namespaces(3) List namespaces) { if (WDT_DOWNLOAD_URL != null) { envMap.put("WDT_INSTALLER_URL", WDT_DOWNLOAD_URL); } + + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + DOMAIN_CREATION_IMAGE_NAME = "localhost/wdt-domain-image"; + envMap.put("OPER_IMAGE_NAME", "localhost/weblogic-kubernetes-operator"); + envMap.put("MODEL_IMAGE_NAME", DOMAIN_CREATION_IMAGE_NAME); + envMap.put("K8S_NODEPORT_HOST", assertDoesNotThrow(() -> InetAddress.getLocalHost().getHostAddress())); + envMap.put("TRAEFIK_INGRESS_HTTP_HOSTPORT", "" + TRAEFIK_INGRESS_HTTP_HOSTPORT); + envMap.put("TRAEFIK_NAMESPACE", TRAEFIK_NAMESPACE); + } else { + envMap.put("TRAEFIK_NAMESPACE", traefikNamespace); + envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); + } + logger.info("Environment variables to the script {0}", envMap); logger.info("Setting up image registry secrets"); @@ -163,7 +183,13 @@ public void testInstallOperator() { @Test @Order(2) public void testInstallTraefik() { - execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + logger.info("skip installing Traefik in KIND and podman environment"); + logger.info("Traefik is already installed in InitialTask in namespace %s", TRAEFIK_NAMESPACE); + } else { + execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + } } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java index e9b07d7af44..1fb5d16b316 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java @@ -3,6 +3,7 @@ package oracle.weblogic.kubernetes; +import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,12 +34,16 @@ import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_REGISTRY; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_NAMESPACE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_JAVA_HOME; @@ -49,6 +54,7 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -64,7 +70,6 @@ class ItWlsMiiSample { private static final String miiSampleScript = "../operator/integration-tests/model-in-image/run-test.sh"; - private static final String DOMAIN_CREATION_IMAGE_NAME = "wdt-domain-image"; private static final String DOMAIN_CREATION_IMAGE_WLS_TAG = "WLS-v1"; private static final String MODEL_IMAGE_WLS_TAG = "WLS-v2"; private static String traefikNamespace = null; @@ -72,6 +77,7 @@ class ItWlsMiiSample { private static LoggingFacade logger = null; private boolean previousTestSuccessful = true; + private static String DOMAIN_CREATION_IMAGE_NAME = "wdt-domain-image"; /** * Create namespaces and set environment variables for the test. @@ -117,7 +123,8 @@ public static void initAll(@Namespaces(3) List namespaces) { envMap.put("BASE_IMAGE_TAG", WEBLOGIC_IMAGE_TAG); envMap.put("IMAGE_PULL_SECRET_NAME", BASE_IMAGES_REPO_SECRET_NAME); envMap.put("DOMAIN_IMAGE_PULL_SECRET_NAME", TEST_IMAGES_REPO_SECRET_NAME); - envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); + envMap.put("WLSIMG_BUILDER_DEFAULT", WLSIMG_BUILDER_DEFAULT); + envMap.put("WLSIMG_BUILDER", WLSIMG_BUILDER); envMap.put("OKD", "" + OKD); envMap.put("KIND_CLUSTER", "" + KIND_CLUSTER); @@ -133,6 +140,19 @@ public static void initAll(@Namespaces(3) List namespaces) { if (WDT_DOWNLOAD_URL != null) { envMap.put("WDT_INSTALLER_URL", WDT_DOWNLOAD_URL); } + + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + DOMAIN_CREATION_IMAGE_NAME = "localhost/wdt-domain-image"; + envMap.put("OPER_IMAGE_NAME", "localhost/weblogic-kubernetes-operator"); + envMap.put("MODEL_IMAGE_NAME", DOMAIN_CREATION_IMAGE_NAME); + envMap.put("K8S_NODEPORT_HOST", assertDoesNotThrow(() -> InetAddress.getLocalHost().getHostAddress())); + envMap.put("TRAEFIK_INGRESS_HTTP_HOSTPORT", "" + TRAEFIK_INGRESS_HTTP_HOSTPORT); + envMap.put("TRAEFIK_NAMESPACE", TRAEFIK_NAMESPACE); + } else { + envMap.put("TRAEFIK_NAMESPACE", traefikNamespace); + envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); + } + logger.info("Environment variables to the script {0}", envMap); logger.info("Setting up image registry secrets"); @@ -163,7 +183,12 @@ public void testInstallOperator() { @Test @Order(2) public void testInstallTraefik() { - execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + logger.info("skip installing Traefik in KIND and podman environment"); + logger.info("Traefik is already installed in InitialTask in namespace %s", TRAEFIK_NAMESPACE); + } else { + execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + } } /** diff --git a/operator/integration-tests/domain-on-pv/test-env.sh b/operator/integration-tests/domain-on-pv/test-env.sh index 2251618a9c8..4d1a42592fd 100755 --- a/operator/integration-tests/domain-on-pv/test-env.sh +++ b/operator/integration-tests/domain-on-pv/test-env.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -62,3 +62,7 @@ export OCNE=${OCNE:-false} # default Kubernetes CLI export KUBERNETES_CLI=${KUBERNETES_CLI:-kubectl} + +# default WLSIMG_BUILDER +export WLSIMG_BUILDER=${WLSIMG_BUILDER:-docker} +export WLSIMG_BUILDER_DEFAULT=${WLSIMG_BUILDER_DEFAULT:-docker} \ No newline at end of file diff --git a/operator/integration-tests/domain-on-pv/util-misc.sh b/operator/integration-tests/domain-on-pv/util-misc.sh index da4748ab8ed..e0589c5c155 100755 --- a/operator/integration-tests/domain-on-pv/util-misc.sh +++ b/operator/integration-tests/domain-on-pv/util-misc.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. timestamp() { @@ -105,7 +105,9 @@ testapp() { local command="${KUBERNETES_CLI:-kubectl} exec -n $ns $admin_service_name -- bash -c \"curl -s -S $(curl_timeout_parms) http://$cluster_service_name:8001/myapp_war/index.jsp\"" elif [ "$1" = "traefik" ]; then - if [ -z "$traefik_nodeport" ]; then + if [ "$KIND_CLUSTER" = "true" ] && [ "$WLSIMG_BUILDER" != "$WLSIMG_BUILDER_DEFAULT" ]; then + traefik_nodeport=${TRAEFIK_INGRESS_HTTP_HOSTPORT:-2080} + elif [ -z "$traefik_nodeport" ]; then echo "@@ Info: Obtaining traefik nodeport by calling:" cat< Date: Fri, 26 Apr 2024 19:53:31 +0000 Subject: [PATCH 047/356] backport MRs 4676, 4671, 4662 to release/4.2 --- Jenkinsfile.podman | 21 ++- .../kubernetes/ItElasticLoggingSample.java | 6 + ...tHorizontalPodAutoscalerCustomMetrics.java | 30 ++- .../kubernetes/ItIntrospectVersion.java | 4 +- .../kubernetes/ItIstioMonitoringExporter.java | 33 +++- .../kubernetes/ItProductionSecureMode.java | 131 ++++++++----- .../oracle/weblogic/kubernetes/ItWseeSSO.java | 177 ++++++++++++------ .../weblogic/kubernetes/TestConstants.java | 25 +++ .../kubernetes/actions/impl/Ingress.java | 7 +- .../actions/impl/LoggingExporter.java | 21 ++- .../kubernetes/utils/CommonMiiTestUtils.java | 24 ++- .../kubernetes/utils/CommonTestUtils.java | 32 +++- .../kubernetes/utils/OracleHttpClient.java | 61 +++++- .../traefik/traefik-ingress-rules-tcp.yaml | 18 ++ 14 files changed, 449 insertions(+), 141 deletions(-) create mode 100644 integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 3ef209c8249..68c04564459 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -20,7 +20,8 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel''' +CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel + H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential''' pipeline { agent { label 'large-ol9' } @@ -373,6 +374,9 @@ pipeline { if kind delete cluster --name ${kind_name} --kubeconfig "${kubeconfig_file}"; then echo "Deleted orphaned kind cluster ${kind_name}" fi + # settings needed by elastic logging tests + echo "running sudo sysctl -w vm.max_map_count=262144" + sudo sysctl -w vm.max_map_count=262144 cat < namespaces) { "elasticsearch:7.8.1", ELASTICSEARCH_IMAGE),"Failed to replace String: " + ELASTICSEARCH_IMAGE); assertDoesNotThrow(() -> replaceStringInFile(destELKConfigFilePath.toString(), "kibana:7.8.1", KIBANA_IMAGE),"Failed to replace String: " + KIBANA_IMAGE); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + assertDoesNotThrow(() -> replaceStringInFile(destELKConfigFilePath.toString(), + "'-w', 'vm.max_map_count=262144'", "'vm.max_map_count'"), + "Failed to replace String: " + "'-w', 'vm.max_map_count=262144'"); + } // install and verify Elasticsearch and Kibana; elasticSearchHost = "elasticsearch." + elasticSearchNs + ".svc.cluster.local"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java index da62f16d131..599e8868d13 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java @@ -4,6 +4,8 @@ package oracle.weblogic.kubernetes; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -47,13 +49,21 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ITHPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITHPACUSTOMNGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; @@ -212,7 +222,8 @@ public static void initAll(@Namespaces(4) List namespaces) { ); // install and verify NGINX - nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + nginxHelmParams = installAndVerifyNginx(nginxNamespace, ITHPACUSTOMNGINX_INGRESS_HTTP_NODEPORT, + ITHPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, (OKE_CLUSTER ? null : "NodePort")); String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; logger.info("NGINX service name: {0}", nginxServiceName); @@ -221,6 +232,14 @@ public static void initAll(@Namespaces(4) List namespaces) { String host = formatIPv6Host(K8S_NODEPORT_HOST); ingressIP = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : host; + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + try { + ingressIP = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException ex) { + logger.severe(ex.getLocalizedMessage()); + } + nodeportshttp = ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; + } // create cluster resouce with limits and requests in serverPod ClusterResource clusterResource = @@ -289,6 +308,15 @@ void testHPAWithCustomMetrics() { //invoke app 20 times to generate metrics with number of opened sessions > 5 String host = formatIPv6Host(K8S_NODEPORT_HOST); String hostPort = OKE_CLUSTER_PRIVATEIP ? ingressIP : host + ":" + nodeportshttp; + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + try { + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException ex) { + logger.severe(ex.getLocalizedMessage()); + } + nodeportshttp = ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; + hostPort = host + ":" + nodeportshttp; + } String curlCmd = String.format("curl --silent --show-error --noproxy '*' -H 'host: %s' http://%s:%s@%s/" + SESSMIGT_APP_URL, ingressHostList.get(0), diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index cae3b976f1b..40aed8a8b08 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -1569,7 +1569,7 @@ private DomainResource createDomainResourceWithConfigMap(String domainUid, Strin } private void updateIngressBackendServicePort(int newAdminPort) throws ApiException { - String ingressName = introDomainNamespace + "-" + domainUid + "-" + adminServerName; + String ingressName = introDomainNamespace + "-" + domainUid + "-" + adminServerName + "-7001"; V1Ingress ingress = Ingress.getIngress(introDomainNamespace, ingressName).orElse(null); if (ingress != null) { logger.info("Updating ingress {0} with new admin port {1}", ingressName, newAdminPort); @@ -1578,7 +1578,7 @@ private void updateIngressBackendServicePort(int newAdminPort) throws ApiExcepti .setPort(new V1ServiceBackendPort().number(newAdminPort)); updateIngress(introDomainNamespace, ingress); } else { - fail("Failed to update ingress"); + fail("Ingress is null, failed to update ingress"); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java index 89569c398e9..9d40da548a3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java @@ -3,6 +3,9 @@ package oracle.weblogic.kubernetes; + +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -24,7 +27,11 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ISTIO_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_CONAINERPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; @@ -38,7 +45,6 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createTestWebAppWarFile; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getImageBuilderExtraArgs; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.startPortForwardProcess; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.stopPortForwardProcess; @@ -140,7 +146,7 @@ public static void initAll(@Namespaces(3) List namespaces) { // install and verify operator installAndVerifyOperator(opNamespace, domain1Namespace, domain2Namespace); - prometheusPort = getNextFreePort(); + prometheusPort = IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_CONAINERPORT; } /** @@ -223,6 +229,14 @@ private void deployPrometheusAndVerify(String domainNamespace, String domainUid, // In internal OKE env, use Istio EXTERNAL-IP; in non-OKE env, use K8S_NODEPORT_HOST + ":" + istioIngressPort hostPortPrometheus = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) : host + ":" + prometheusPort; + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !OCNE) { + try { + hostPortPrometheus = InetAddress.getLocalHost().getHostAddress() + + ":" + IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_HOSTPORT; + } catch (UnknownHostException ex) { + logger.severe(ex.getLocalizedMessage()); + } + } if (OKE_CLUSTER_PRIVATEIP) { String localhost = "localhost"; @@ -233,7 +247,6 @@ private void deployPrometheusAndVerify(String domainNamespace, String domainUid, logger.info("Forwarded local port is {0}", forwardPort); hostPortPrometheus = localhost + ":" + forwardPort; isPrometheusPortForward = true; - } } else { String newRegex = String.format("regex: %s;%s", domainNamespace, domainUid); @@ -394,7 +407,17 @@ private void setupIstioModelInImageDomain(String miiImage, String domainNamespac // In internal OKE env, use Istio EXTERNAL-IP; in non-OKE env, use K8S_NODEPORT_HOST + ":" + istioIngressPort String hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) : host + ":" + istioIngressPort; - + String deployHost = K8S_NODEPORT_HOST; + + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !OCNE) { + istioIngressPort = ISTIO_HTTP_HOSTPORT; + try { + hostAndPort = InetAddress.getLocalHost().getHostAddress() + ":" + istioIngressPort; + deployHost = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException ex) { + logger.severe(ex.getLocalizedMessage()); + } + } String readyAppUrl = "http://" + hostAndPort + "/weblogic/ready"; boolean checlReadyApp = checkAppUsingHostHeader(readyAppUrl, domainNamespace + ".org"); @@ -406,7 +429,7 @@ private void setupIstioModelInImageDomain(String miiImage, String domainNamespac ExecResult result = OKE_CLUSTER ? deployUsingRest(hostAndPort, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, target, archivePath, domainNamespace + ".org", "testwebapp") - : deployToClusterUsingRest(K8S_NODEPORT_HOST, + : deployToClusterUsingRest(deployHost, String.valueOf(istioIngressPort), ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, clusterName, archivePath, domainNamespace + ".org", "testwebapp"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java index f6424590656..9a36a1b8db9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java @@ -3,6 +3,8 @@ package oracle.weblogic.kubernetes; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -23,10 +25,13 @@ import oracle.weblogic.domain.Model; import oracle.weblogic.domain.OnlineUpdate; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.ActionConstants; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -38,6 +43,7 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_DEPLOYMENT_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; @@ -45,7 +51,6 @@ import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; -import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_SLIM; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.createDomainCustomResource; @@ -187,6 +192,25 @@ public static void initAll(@Namespaces(2) List namespaces) { domainNamespace); } + @AfterAll + public static void cleanup() { + Path dstFile = Paths.get(TestConstants.RESULTS_ROOT, "traefik/traefik-ingress-rules-tcp.yaml"); + assertDoesNotThrow(() -> { + String command = KUBERNETES_CLI + " delete -f " + dstFile; + logger.info("Running {0}", command); + ExecResult result; + try { + result = ExecCommand.exec(command, true); + String response = result.stdout().trim(); + logger.info("exitCode: {0}, \nstdout: {1}, \nstderr: {2}", + result.exitValue(), response, result.stderr()); + assertEquals(0, result.exitValue(), "Command didn't succeed"); + } catch (IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + }); + } + /** * Verify all server pods are running. * Verify all k8s services for all servers are created. @@ -251,49 +275,36 @@ void testVerifyProductionSecureMode() { logger.info("The hostAndPort is {0}", hostAndPort); String resourcePath = "/weblogic/ready"; - if (!WEBLOGIC_SLIM) { - if (OKE_CLUSTER) { - ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName,7002, resourcePath); - logger.info("result in OKE_CLUSTER is {0}", result.toString()); - assertEquals(0, result.exitValue(), "Failed to access WebLogic readyapp"); - } else { - String curlCmd = "curl -g -sk --show-error --noproxy '*' " - + " https://" + hostAndPort - + "/weblogic/ready --write-out %{http_code} " - + " -o /dev/null"; - logger.info("Executing default-admin nodeport curl command {0}", curlCmd); - assertTrue(callWebAppAndWaitTillReady(curlCmd, 10)); - } - logger.info("WebLogic readyapp is accessible thru default-admin service"); - - String localhost = "localhost"; - String forwardPort = startPortForwardProcess(localhost, domainNamespace, domainUid, 9002); - assertNotNull(forwardPort, "port-forward fails to assign local port"); - logger.info("Forwarded admin-port is {0}", forwardPort); - String curlCmd = "curl -sk --show-error --noproxy '*' " - + " https://" + localhost + ":" + forwardPort - + "/weblogic/ready --write-out %{http_code} " - + " -o /dev/null"; - logger.info("Executing default-admin port-fwd curl command {0}", curlCmd); - assertTrue(callWebAppAndWaitTillReady(curlCmd, 10)); - logger.info("WebLogic readyapp is accessible thru admin port forwarding"); - - // When port-forwarding is happening on admin-port, port-forwarding will - // not work for SSL port i.e. 7002 - forwardPort = startPortForwardProcess(localhost, domainNamespace, domainUid, 7002); - assertNotNull(forwardPort, "port-forward fails to assign local port"); - logger.info("Forwarded ssl port is {0}", forwardPort); - curlCmd = "curl -g -sk --show-error --noproxy '*' " - + " https://" + localhost + ":" + forwardPort - + "/weblogic/ready --write-out %{http_code} " - + " -o /dev/null"; - logger.info("Executing default-admin port-fwd curl command {0}", curlCmd); - assertFalse(callWebAppAndWaitTillReady(curlCmd, 10)); - logger.info("WebLogic readyapp should not be accessible thru ssl port forwarding"); - stopPortForwardProcess(domainNamespace); - } else { - logger.info("Skipping WebLogic reeadyapp check in WebLogic slim image"); - } + ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7002, resourcePath); + logger.info("result in OKE_CLUSTER is {0}", result.toString()); + assertEquals(0, result.exitValue(), "Failed to access WebLogic readyapp"); + logger.info("WebLogic readyapp is accessible thru default-admin service"); + + String localhost = "localhost"; + String forwardPort = startPortForwardProcess(localhost, domainNamespace, domainUid, 9002); + assertNotNull(forwardPort, "port-forward fails to assign local port"); + logger.info("Forwarded admin-port is {0}", forwardPort); + String curlCmd = "curl -sk --show-error --noproxy '*' " + + " https://" + localhost + ":" + forwardPort + + "/weblogic/ready --write-out %{http_code} " + + " -o /dev/null"; + logger.info("Executing default-admin port-fwd curl command {0}", curlCmd); + assertTrue(callWebAppAndWaitTillReady(curlCmd, 10)); + logger.info("WebLogic readyapp is accessible thru admin port forwarding"); + + // When port-forwarding is happening on admin-port, port-forwarding will + // not work for SSL port i.e. 7002 + forwardPort = startPortForwardProcess(localhost, domainNamespace, domainUid, 7002); + assertNotNull(forwardPort, "port-forward fails to assign local port"); + logger.info("Forwarded ssl port is {0}", forwardPort); + curlCmd = "curl -g -sk --show-error --noproxy '*' " + + " https://" + localhost + ":" + forwardPort + + "/weblogic/ready --write-out %{http_code} " + + " -o /dev/null"; + logger.info("Executing default-admin port-fwd curl command {0}", curlCmd); + assertFalse(callWebAppAndWaitTillReady(curlCmd, 10)); + logger.info("WebLogic readyapp should not be accessible thru ssl port forwarding"); + stopPortForwardProcess(domainNamespace); int nodePort = getServiceNodePort( domainNamespace, getExternalServicePodName(adminServerPodName), "default"); @@ -337,6 +348,11 @@ void testMiiDynamicChangeWithSSLEnabled() { String introspectVersion = patchDomainResourceWithNewIntrospectVersion(domainUid, domainNamespace); verifyIntrospectorRuns(domainUid, domainNamespace); + String sslChannelName = "default-admin"; + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + createTraefikIngressRoutingRules(domainNamespace); + } String resourcePath = "/management/weblogic/latest/domainRuntime/serverRuntimes/" + MANAGED_SERVER_NAME_BASE + "1" @@ -356,7 +372,7 @@ void testMiiDynamicChangeWithSSLEnabled() { + MANAGED_SERVER_NAME_BASE + "1" + "/applicationRuntimes/" + MII_BASIC_APP_DEPLOYMENT_NAME + "/workManagerRuntimes/newWM", - "200", true, "default-admin"), + "200", true, sslChannelName), logger, "work manager configuration to be updated."); } @@ -423,4 +439,29 @@ private static void createDomainResource( assertTrue(domCreated, String.format("Create domain custom resource failed with ApiException " + "for %s in namespace %s", domainUid, domNamespace)); } + + private static void createTraefikIngressRoutingRules(String domainNamespace) { + logger.info("Creating ingress rules for domain traffic routing"); + Path srcFile = Paths.get(ActionConstants.RESOURCE_DIR, "traefik/traefik-ingress-rules-tcp.yaml"); + Path dstFile = Paths.get(TestConstants.RESULTS_ROOT, "traefik/traefik-ingress-rules-tcp.yaml"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(dstFile); + Files.createDirectories(dstFile.getParent()); + Files.write(dstFile, Files.readString(srcFile).replaceAll("@NS@", domainNamespace) + .getBytes(StandardCharsets.UTF_8)); + }); + String command = KUBERNETES_CLI + " create -f " + dstFile; + logger.info("Running {0}", command); + ExecResult result; + try { + result = ExecCommand.exec(command, true); + String response = result.stdout().trim(); + logger.info("exitCode: {0}, \nstdout: {1}, \nstderr: {2}", + result.exitValue(), response, result.stderr()); + assertEquals(0, result.exitValue(), "Command didn't succeed"); + } catch (IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + } + } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java index 1f885304ccc..f3451575300 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java @@ -1,10 +1,11 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; import java.io.IOException; -import java.net.http.HttpResponse; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -35,19 +36,28 @@ import org.junit.jupiter.api.Test; import static java.nio.file.Paths.get; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.ITWSEESSONGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITWSEESSONGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.ITWSEESSONGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -60,7 +70,10 @@ import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createDomainSecret; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createJobToChangePermissionsOnPvHostPath; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressPathRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runClientInsidePodVerifyResult; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runJavacInsidePod; @@ -84,6 +97,7 @@ import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.apache.commons.io.FileUtils.deleteDirectory; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -93,7 +107,7 @@ */ @DisplayName("Verify that client can communicate with webservices with SSO") @IntegrationTest -@Tag("oke-parallel") +@Tag("oke-gate") @Tag("kind-parallel") class ItWseeSSO { @@ -111,15 +125,10 @@ class ItWseeSSO { final String domain2Uid = "mywseedomain2"; final String clusterName = "cluster-1"; final String adminServerName = "admin-server"; - private static List ingressHost1List = null; final String adminServerPodName1 = domain1Uid + "-" + adminServerName; final String adminServerPodName2 = domain2Uid + "-" + adminServerName; final String managedServerNameBase = "managed-server"; Path keyStoresPath; - final int managedServerPort = 8001; - int t3ChannelPort; - - final String wlSecretName = "weblogic-credentials"; int replicaCount = 2; String adminSvcExtHost1 = null; @@ -127,6 +136,7 @@ class ItWseeSSO { static Path wseeServiceAppPath; static Path wseeServiceRefAppPath; static Path wseeServiceRefStubsPath; + private String ingressIP; private static LoggingFacade logger = null; @@ -162,13 +172,16 @@ public void initAll(@Namespaces(4) List namespaces) { installAndVerifyOperator(opNamespace, domain1Namespace, domain2Namespace); if (!OKD) { // install and verify NGINX - nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + nginxHelmParams = installAndVerifyNginx(nginxNamespace, ITWSEESSONGINX_INGRESS_HTTP_NODEPORT, + ITWSEESSONGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, (OKE_CLUSTER ? null : "NodePort")); String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; logger.info("NGINX service name: {0}", nginxServiceName); + + ingressIP = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null + ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST; nodeportshttp = getServiceNodePort(nginxNamespace, nginxServiceName, "http"); logger.info("NGINX http node port: {0}", nodeportshttp); - } keyStoresPath = Paths.get(RESULTS_ROOT, "mydomainwsee", "keystores"); assertDoesNotThrow(() -> deleteDirectory(keyStoresPath.toFile())); @@ -183,6 +196,7 @@ public void initAll(@Namespaces(4) List namespaces) { if (adminSvcExtHost2 == null) { adminSvcExtHost2 = createRouteForOKD(getExternalServicePodName(adminServerPodName1), domain2Namespace); } + // build the wsee application Path distDir = buildApplication(Paths.get(APP_DIR, "wsee"), null, null, "dist", domain1Namespace); @@ -202,45 +216,57 @@ public void initAll(@Namespaces(4) List namespaces) { */ @Test @DisplayName("Test Wsee connect with sso") - void testInvokeWsee() { + void testInvokeWsee() throws Exception { //deploy application to view server configuration deployApplication(clusterName + "," + adminServerName, domain2Namespace, domain2Uid, wseeServiceAppPath); //deploy application to view server configuration deployApplication(clusterName + "," + adminServerName, domain1Namespace, domain1Uid, wseeServiceRefAppPath); - receiverURI = checkWSDLAccess(domain2Namespace, domain2Uid, adminSvcExtHost2, "/samlSenderVouches/EchoService"); - senderURI = checkWSDLAccess(domain1Namespace, domain1Uid, adminSvcExtHost1, "/EchoServiceRef/Echo"); - assertDoesNotThrow(() -> callPythonScript(domain1Uid, domain1Namespace, - "addSAMLRelyingPartySenderConfig.py", receiverURI), + receiverURI = checkWSDLAccess(domain2Namespace, domain2Uid, adminSvcExtHost2, + "/samlSenderVouches/EchoService"); + senderURI = checkWSDLAccess(domain1Namespace, domain1Uid, adminSvcExtHost1, + "/EchoServiceRef/Echo"); + + testUntil(() -> callPythonScript(domain1Uid, domain1Namespace, + "addSAMLRelyingPartySenderConfig.py", receiverURI), + logger, "Failed to run python script addSAMLRelyingPartySenderConfig.py"); + int serviceNodePort = assertDoesNotThrow(() - -> getServiceNodePort(domain2Namespace, getExternalServicePodName(adminServerPodName2), + -> getServiceNodePort(domain2Namespace, getExternalServicePodName(adminServerPodName2), "default"), "Getting admin server node port failed"); - assertDoesNotThrow(() -> callPythonScript(domain1Uid, domain1Namespace, - "setupPKI.py", K8S_NODEPORT_HOST + " " + serviceNodePort), - "Failed to run python script setupPKI.py"); + String hostPort = OKE_CLUSTER_PRIVATEIP ? ingressIP + " 80" : K8S_NODEPORT_HOST + " " + serviceNodePort; + testUntil(() -> callPythonScript(domain1Uid, domain1Namespace, "setupPKI.py", hostPort), + logger, "Failed to run python script setupPKI.py"); buildRunClientOnPod(); } private String checkWSDLAccess(String domainNamespace, String domainUid, - String adminSvcExtHost, - String appURI) { + String adminSvcExtHost, + String appURI) throws UnknownHostException, IOException, InterruptedException { String adminServerPodName = domainUid + "-" + adminServerName; - int serviceNodePort = assertDoesNotThrow(() - -> getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), - "default"), - "Getting admin server node port failed"); - - - logger.info("admin svc host = {0}", adminSvcExtHost); - String hostAndPort = getHostAndPort(adminSvcExtHost, serviceNodePort); + String hostAndPort; + if (!OKE_CLUSTER_PRIVATEIP) { + int serviceTestNodePort = assertDoesNotThrow(() + -> getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"), + "Getting admin server node port failed"); + logger.info("admin svc host = {0}", adminSvcExtHost); + hostAndPort = getHostAndPort(adminSvcExtHost, serviceTestNodePort); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + // to access app url in podman we have to use mapped nodeport and localhost + String url = "http://" + formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + + ":" + ITWSEESSONGINX_INGRESS_HTTP_HOSTPORT + appURI; + assertEquals(200, OracleHttpClient.get(url, true).statusCode()); + // to access app url inside admin pod we have to use nodehost and nodeport + return "http://" + K8S_NODEPORT_HOST + ":" + serviceTestNodePort + appURI; + } + } else { + hostAndPort = ingressIP + ":80"; + } String url = "http://" + hostAndPort + appURI; - - HttpResponse response = assertDoesNotThrow(() -> OracleHttpClient.get(url, true)); - assertTrue(response.statusCode() == 200); - logger.info(response.body()); + assertEquals(200, OracleHttpClient.get(url, true).statusCode()); return url; } @@ -256,9 +282,6 @@ private void createDomain(String domainNamespace, String domainUid, String model // let the user name be something other than weblogic say wlsadmin logger.info("Create secret for admin credentials"); String adminSecretName = "weblogic-credentials"; - //assertDoesNotThrow(() -> createDomainSecret(adminSecretName, - // "wlsadmin", "##W%*}!\"'\"`']\\\\//1$$~x", domainNamespace), - // String.format("createSecret failed for %s", adminSecretName)); assertDoesNotThrow(() -> createSecretWithUsernamePassword(adminSecretName, domainNamespace, "weblogic", "welcome1"), String.format("create secret for admin credentials failed for %s", adminSecretName)); @@ -270,21 +293,15 @@ private void createDomain(String domainNamespace, String domainUid, String model String.format("createSecret failed for %s", encryptionSecretName)); String configMapName = "mii-ssl-configmap"; - // Copy the model file to RESULTS_ROOT - assertDoesNotThrow(() -> java.nio.file.Files.copy( - Paths.get(MODEL_DIR, modelFileName), - Paths.get(RESULTS_ROOT, modelFileName), - java.nio.file.StandardCopyOption.REPLACE_EXISTING), - "Copy mii.ssl.yaml to RESULTS_ROOT failed"); String jksMountPath = "/shared/" + domainNamespace + "/" + domainUid + "/keystores"; assertDoesNotThrow(() -> - replaceStringInFile(get(RESULTS_ROOT, modelFileName).toString(), + replaceStringInFile(get(modelFileName).toString(), "/shared/", jksMountPath + "/")); createConfigMapAndVerify( configMapName, domainUid, domainNamespace, - Arrays.asList(RESULTS_ROOT + "/" + modelFileName)); + Arrays.asList(modelFileName)); // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); @@ -419,26 +436,43 @@ private static void copyKeyStores(String domainNamespace, //create a standard WebLogic domain. private void createDomains() { - createDomain(domain1Namespace, domain1Uid, WDT_MODEL_FILE_SENDER); - createDomain(domain2Namespace, domain2Uid, WDT_MODEL_FILE_RECEIVER); + // Generate the model.sessmigr.yaml file at RESULTS_ROOT + String destYamlFile1 = + generateNewModelFileWithUpdatedProps(domain1Namespace, domain1Uid, "ItWseeSSO", + WDT_MODEL_FILE_SENDER); + String destYamlFile2 = + generateNewModelFileWithUpdatedProps(domain2Namespace, domain2Uid, "ItWseeSSO", + WDT_MODEL_FILE_RECEIVER); + createDomain(domain1Namespace, domain1Uid, destYamlFile1); + createDomain(domain2Namespace, domain2Uid, destYamlFile2); + if (!OKD) { + String ingressClassName = nginxHelmParams.getIngressClassName(); + createIngressPathRouting(domain1Namespace, "/EchoServiceRef", + domain1Uid + "-admin-server", 7001, ingressClassName); + createIngressPathRouting(domain2Namespace, "/samlSenderVouches", + domain2Uid + "-admin-server", 7001, ingressClassName); + } if (adminSvcExtHost1 == null) { adminSvcExtHost1 = createRouteForOKD(getExternalServicePodName(adminServerPodName1), domain1Namespace); } if (adminSvcExtHost2 == null) { adminSvcExtHost2 = createRouteForOKD(getExternalServicePodName(adminServerPodName2), domain2Namespace); } - - assertDoesNotThrow(() -> callPythonScript(domain1Uid, domain1Namespace, + testUntil(() -> callPythonScript(domain1Uid, domain1Namespace, "setupAdminSSL.py", "mykeysen changeit 7002 /shared/" + domain1Namespace + "/" + domain1Uid + "/keystores Identity1KeyStore.jks"), + logger, "Failed to run python script setupAdminSSL.py"); - assertDoesNotThrow(() -> callPythonScript(domain2Uid, domain2Namespace, + + testUntil(() -> callPythonScript(domain2Uid, domain2Namespace, "setupAdminSSL.py", "mykeyrec changeit 7002 /shared/" + domain2Namespace + "/" + domain2Uid + "/keystores Identity2KeyStore.jks"), + logger, "Failed to run python script setupAdminSSL.py"); - assertDoesNotThrow(() -> callPythonScript(domain2Uid, domain2Namespace, + testUntil(() -> callPythonScript(domain2Uid, domain2Namespace, "addSAMLAssertingPartyReceiverConfig.py", "/samlSenderVouches/EchoService"), + logger, "Failed to run python script addSAMLAssertingPartyReceiverConfig.py"); } @@ -542,10 +576,14 @@ private boolean callPythonScript(String domainUid, .append(param) .toString(); - result = exec(adminPod, null, true, "/bin/sh", "-c", command); - if (result.exitValue() != 0) { - return false; - } + testUntil(() -> { + ExecResult result1 = exec(adminPod, null, true, "/bin/sh", "-c", command); + if (result1.exitValue() != 0) { + return false; + } else { + return true; + } + }, logger, " Command returns unexpected exit value"); return true; } @@ -594,4 +632,35 @@ private static V1Pod setupWebLogicPod(String namespace, String pvName, return wlsPod; } + + public static String generateNewModelFileWithUpdatedProps(String domainUid, + String namespace, + String className, + String origModelFile) { + final String srcModelYamlFile = MODEL_DIR + "/" + origModelFile; + final String destModelYamlFile = RESULTS_ROOT + "/" + domainUid + "/" + className + "/" + origModelFile; + Path srcModelYamlPath = Paths.get(srcModelYamlFile); + Path destModelYamlPath = Paths.get(destModelYamlFile); + + // create dest dir + assertDoesNotThrow(() -> Files.createDirectories( + Paths.get(RESULTS_ROOT + "/" + domainUid, className)), + String.format("Could not create directory under %s", RESULTS_ROOT + "/" + + domainUid + className + "")); + + // copy model.yaml to results dir + assertDoesNotThrow(() -> Files.copy(srcModelYamlPath, destModelYamlPath, REPLACE_EXISTING), + "Failed to copy " + srcModelYamlFile + " to " + destModelYamlFile); + + // DOMAIN_NAME in model.yaml + assertDoesNotThrow(() -> replaceStringInFile( + destModelYamlFile.toString(), "DOMAIN_NAME", domainUid), + "Could not modify DOMAIN_NAME in " + destModelYamlFile); + // NAMESPACE in model.yaml + assertDoesNotThrow(() -> replaceStringInFile( + destModelYamlFile.toString(), "NAMESPACE", namespace), + "Could not modify NAMESPACE in " + destModelYamlFile); + + return destModelYamlFile; + } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 48a6bf117b1..130b9f034fa 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -474,16 +474,41 @@ public interface TestConstants { //node ports used by the integration tests public static final int ITEXTERNALNODEPORTSERVICE_CONAINERPORT = 32156; public static final int ITEXTERNALNODEPORTSERVICE_HOSTPORT = 2156; + public static final int IT_DEDICATED_MODE_CONAINERPORT = 32159; public static final int IT_DEDICATED_MODE_HOSTPORT = 2160; + public static final int IT_EXTERNALLB_TUNNELING_HTTP_CONAINERPORT = 32169; public static final int IT_EXTERNALLB_TUNNELING_HTTP_HOSTPORT = 2172; public static final int IT_EXTERNALLB_TUNNELING_HTTPS_CONAINERPORT = 32170; public static final int IT_EXTERNALLB_TUNNELING_HTTPS_HOSTPORT = 2173; + + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_CONAINERPORT = 32189; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_HOSTPORT = 2182; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_CONAINERPORT = 32185; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_HOSTPORT = 2175; + + public static final int IT_MONITORINGEXPORTER_PROM_HTTP_CONAINERPORT = 32143; + public static final int IT_MONITORINGEXPORTER_PROM_HTTP_HOSTPORT = 2143; + public static final int IT_MONITORINGEXPORTER_ALERT_HTTP_CONAINERPORT = 32343; + public static final int IT_MONITORINGEXPORTER_ALERT_HTTP_HOSTPORT = 2343; + + public static final int IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_CONAINERPORT = 32331; + public static final int IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_HOSTPORT = 2331; public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT = 30881; public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT = 30444; public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT = 2081; public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT = 2444; + + public static final int ITWSEESSONGINX_INGRESS_HTTP_NODEPORT = 31781; + public static final int ITWSEESSONGINX_INGRESS_HTTPS_NODEPORT = 31744; + public static final int ITWSEESSONGINX_INGRESS_HTTP_HOSTPORT = 2781; + public static final int ITWSEESSONGINX_INGRESS_HTTPS_HOSTPORT = 2782; + + public static final int ITHPACUSTOMNGINX_INGRESS_HTTP_NODEPORT = 31785; + public static final int ITHPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT = 31746; + public static final int ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT = 2785; + public static final int ITHPACUSTOMNGINX_INGRESS_HTTPS_HOSTPORT = 2786; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Ingress.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Ingress.java index eea0b73a82c..0eac0105882 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Ingress.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Ingress.java @@ -25,6 +25,7 @@ import static oracle.weblogic.kubernetes.actions.ActionConstants.INGRESS_API_VERSION; import static oracle.weblogic.kubernetes.actions.ActionConstants.INGRESS_KIND; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listNamespacedIngresses; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; /** @@ -240,11 +241,7 @@ public static List listIngresses(String namespace) throws ApiException { * @throws ApiException if Kubernetes client API call fails */ public static Optional getIngress(String namespace, String ingressName) throws ApiException { - - V1IngressList ingressList = Kubernetes.listNamespacedIngresses(namespace); - List listOfIngress = ingressList.getItems(); - - return listOfIngress.stream().filter( + return listNamespacedIngresses(namespace).getItems().stream().filter( ingress -> ingress.getMetadata().getName().equals(ingressName)).findAny(); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java index b21d984358b..0073d1ebbda 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java @@ -1,8 +1,9 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.actions.impl; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -25,6 +26,7 @@ import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServicePort; import io.kubernetes.client.openapi.models.V1ServiceSpec; +import oracle.weblogic.kubernetes.TestConstants; import oracle.weblogic.kubernetes.actions.impl.primitive.Installer; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.assertions.impl.Deployment; @@ -335,6 +337,16 @@ private static V1Deployment createElasticsearchDeploymentCr(LoggingExporterParam Map labels = new HashMap<>(); labels.put("app", elasticsearchName); + List initcontainers = new ArrayList<>(); + if (TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + V1Container setmaxmap = new V1Container() + .name("set-vm-max-map-count") + .image(BUSYBOX_IMAGE + ":" + BUSYBOX_TAG) + .imagePullPolicy(IMAGE_PULL_POLICY) + .command(Arrays.asList("sysctl", "-w", "vm.max_map_count=262144")) + .securityContext(new V1SecurityContext().privileged(true)); + initcontainers.add(setmaxmap); + } // create Elasticsearch deployment CR object V1Deployment elasticsearchDeployment = new V1Deployment() .apiVersion("apps/v1") @@ -351,12 +363,7 @@ private static V1Deployment createElasticsearchDeploymentCr(LoggingExporterParam .metadata(new V1ObjectMeta() .labels(labels)) .spec(new V1PodSpec() - .initContainers(List.of(new V1Container() - .name("set-vm-max-map-count") - .image(BUSYBOX_IMAGE + ":" + BUSYBOX_TAG) - .imagePullPolicy(IMAGE_PULL_POLICY) - .command(Arrays.asList("sysctl", "-w", "vm.max_map_count=262144")) - .securityContext(new V1SecurityContext().privileged(true)))) + .initContainers(initcontainers) .containers(List.of(new V1Container() .name(elasticsearchName) .image(elasticsearchImage) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index cbba7cfadb7..4b5b1d56e04 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -4,6 +4,7 @@ package oracle.weblogic.kubernetes.utils; import java.io.IOException; +import java.net.InetAddress; import java.net.http.HttpResponse; import java.time.OffsetDateTime; import java.util.ArrayList; @@ -18,6 +19,7 @@ import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; +import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1Job; @@ -70,6 +72,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; @@ -96,6 +99,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyCredentials; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; @@ -1245,15 +1249,15 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost, } String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } + formatIPv6Host(host); String hostAndPort = (OKD) ? adminSvcExtHost : host + ":" + adminServiceNodePort; logger.info("hostAndPort = {0} ", hostAndPort); if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - int port = getServicePort(domainNamespace, adminServerPodName, "internal-t3"); + String channel = "internal-t3"; + int port = getServicePort(domainNamespace, getExternalServicePodName(adminServerPodName), + sslChannelName.isEmpty() ? channel : sslChannelName); String domainName = adminServerPodName.split("-" + ADMIN_SERVER_NAME_BASE)[0]; String serviceName = ADMIN_SERVER_NAME_BASE; String ingressName = domainNamespace + "-" + domainName + "-" + serviceName + "-" + port; @@ -1262,19 +1266,21 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost, try { List ingresses = TestActions.listIngresses(domainNamespace); ingressFound = ingresses.stream().filter(ingress -> ingress.equals(ingressName)).findAny(); - if (ingressFound.isEmpty()) { + if (ingressFound.isEmpty() && sslChannelName.isEmpty()) { createIngressHostRouting(domainNamespace, domainName, serviceName, port); } else { - logger.info("Ingress {0} found, skipping ingress resource creation...", ingressFound); + logger.info("Ingress {0} found or secure channel, skipping ingress resource creation...", ingressFound); } - } catch (Exception ex) { + } catch (ApiException ex) { logger.severe(ex.getMessage()); } - hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; + hostAndPort = assertDoesNotThrow(() + -> formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + + (isSecureMode ? TRAEFIK_INGRESS_HTTPS_HOSTPORT : TRAEFIK_INGRESS_HTTP_HOSTPORT)); Map headers = new HashMap<>(); headers.put("host", hostHeader); headers.put("Authorization", ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT); - String url = "http://" + hostAndPort + resourcePath; + String url = (isSecureMode ? "https" : "http") + "://" + hostAndPort + resourcePath; HttpResponse response; try { response = OracleHttpClient.get(url, headers, true); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index f552302cc50..71c66ec6e4e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -23,6 +23,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.Random; @@ -37,6 +38,7 @@ import io.kubernetes.client.openapi.models.V1IngressBackend; import io.kubernetes.client.openapi.models.V1IngressRule; import io.kubernetes.client.openapi.models.V1IngressServiceBackend; +import io.kubernetes.client.openapi.models.V1IngressTLS; import io.kubernetes.client.openapi.models.V1ServiceBackendPort; import oracle.weblogic.domain.ClusterSpec; import oracle.weblogic.domain.DomainCondition; @@ -2339,10 +2341,27 @@ public static Callable isAppInServerPodReady(String domainNamespace, * @param domainUid domain resource name * @param serviceName name of the service for which to create ingress routing * @param port container port of the service - * @return hostheader + * @return hostheader host header */ public static String createIngressHostRouting(String domainNamespace, String domainUid, String serviceName, int port) { + return createIngressHostRouting(domainNamespace, domainUid, serviceName, port, null, null, false); + } + + /** + * Create ingress resource for a single service. + * + * @param domainNamespace namespace in which the service exists + * @param domainUid domain resource name + * @param serviceName name of the service for which to create ingress routing + * @param port container port of the service + * @param annoations ingress annotations + * @param tlsList list of tls secrets + * @param isSecureMode if TLS + * @return hostheader host header + */ + public static String createIngressHostRouting(String domainNamespace, String domainUid, + String serviceName, int port, Map annoations, List tlsList, boolean isSecureMode) { // create an ingress in domain namespace // set the ingress rule host String ingressHost = domainNamespace + "." + domainUid + "." + serviceName; @@ -2366,17 +2385,18 @@ public static String createIngressHostRouting(String domainNamespace, String dom ingressRules.add(ingressRule); String ingressName = domainNamespace + "-" + domainUid + "-" + serviceName + '-' + port; - assertDoesNotThrow(() -> createIngress(ingressName, domainNamespace, null, - Files.readString(INGRESS_CLASS_FILE_NAME), ingressRules, null)); + assertDoesNotThrow(() -> createIngress(ingressName, domainNamespace, annoations, + Files.readString(INGRESS_CLASS_FILE_NAME), ingressRules, tlsList)); // check the ingress was found in the domain namespace assertThat(assertDoesNotThrow(() -> listIngresses(domainNamespace))) .as(String.format("Test ingress %s was found in namespace %s", ingressName, domainNamespace)) .withFailMessage(String.format("Ingress %s was not found in namespace %s", ingressName, domainNamespace)) .contains(ingressName); - String curlCmd = "curl -g --silent --show-error --noproxy '*' -H 'host: " + ingressHost - + "' http://localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT - + "/weblogic/ready --write-out %{http_code} -o /dev/null"; + String curlCmd = assertDoesNotThrow(() -> "curl -g -k --silent --show-error --noproxy '*' -H 'host: " + + ingressHost + "' " + (isSecureMode ? "https" : "http") + "://" + + formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + +TRAEFIK_INGRESS_HTTP_HOSTPORT + + "/weblogic/ready --write-out %{http_code} -o /dev/null"); getLogger().info("Executing curl command {0}", curlCmd); assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java index 152ecfd5d96..13bfc118e0d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java @@ -4,14 +4,22 @@ package oracle.weblogic.kubernetes.utils; import java.io.IOException; +import java.net.Socket; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.time.Duration; import java.util.Base64; import java.util.Map; import java.util.Map.Entry; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -23,12 +31,6 @@ */ public class OracleHttpClient { - private static final HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(HttpClient.Redirect.NORMAL) - .build(); - /** * Http GET request. * @@ -42,6 +44,21 @@ public class OracleHttpClient { public static HttpResponse get(String url, Map headers, boolean debug) throws IOException, InterruptedException { LoggingFacade logger = getLogger(); + + SSLContext sslContext = null; + try { + sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new TrustManager[]{MOCK_TRUST_MANAGER}, new SecureRandom()); + } catch (Exception ex) { + logger.severe(ex.getLocalizedMessage()); + } + HttpClient httpClient = HttpClient.newBuilder() + .sslContext(sslContext) + .version(HttpClient.Version.HTTP_1_1) + .connectTimeout(Duration.ofSeconds(30)) + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(); requestBuilder .GET() @@ -109,4 +126,36 @@ public static HttpResponse get(String url, Map headers) return get(url, headers, false); } + private static final TrustManager MOCK_TRUST_MANAGER = new X509ExtendedTrustManager() { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[0]; + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + }; + } \ No newline at end of file diff --git a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml new file mode 100644 index 00000000000..33a428650e6 --- /dev/null +++ b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRouteTCP +metadata: + name: production-secure-tls-passthrough + namespace: @NS@ +spec: + entryPoints: + - websecure + routes: + - match: HostSNI(`*`) + services: + - name: mii-default-admin-admin-server + port: 9002 + tls: + passthrough: true From a0eb73c9ef616dda4afc4ca84aee165e560b9814 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Fri, 26 Apr 2024 20:57:25 +0000 Subject: [PATCH 048/356] backport MR 4668 and 4657 to release/4.2 --- .../weblogic/kubernetes/ItVzWlsDomainOnPV.java | 5 +++-- .../kubernetes/ItAddNewDynamicClusterUsingWlst.java | 4 +++- .../kubernetes/ItConfigDistributionStrategy.java | 8 +++----- .../kubernetes/ItFmwDomainInPVUsingWDT.java | 3 ++- .../kubernetes/ItFmwDomainInPVUsingWLST.java | 3 ++- .../kubernetes/ItFmwDomainInPvUserCreateRcu.java | 3 ++- .../oracle/weblogic/kubernetes/ItFmwDomainOnPV.java | 3 ++- .../weblogic/kubernetes/ItFmwDynamicDomainInPV.java | 3 ++- .../weblogic/kubernetes/ItIntrospectVersion.java | 13 ++++++++----- .../weblogic/kubernetes/ItIstioDomainInPV.java | 4 +++- .../kubernetes/ItKubernetesDomainEvents.java | 6 ++++-- .../weblogic/kubernetes/ItLBTwoDomainsNginx.java | 5 +++-- .../weblogic/kubernetes/ItLBTwoDomainsTraefik.java | 5 +++-- .../kubernetes/ItLivenessProbeCustomization.java | 5 +++-- .../kubernetes/ItMiiDomainUpgradeToSecureMode.java | 6 +++--- .../kubernetes/ItMonitoringExporterSamples.java | 3 ++- .../weblogic/kubernetes/ItOperatorFmwUpgrade.java | 5 +++-- .../weblogic/kubernetes/ItRecoveryDomainInPV.java | 5 +++-- .../weblogic/kubernetes/ItSystemResOverrides.java | 5 +++-- .../oracle/weblogic/kubernetes/ItT3Channel.java | 3 ++- .../oracle/weblogic/kubernetes/ItWlsDomainOnPV.java | 5 +++-- .../oracle/weblogic/kubernetes/TestConstants.java | 8 ++++++-- .../weblogic/kubernetes/utils/CommonTestUtils.java | 4 +++- .../weblogic/kubernetes/utils/DeployUtil.java | 4 +++- .../weblogic/kubernetes/utils/DomainUtils.java | 9 ++++++--- 25 files changed, 80 insertions(+), 47 deletions(-) diff --git a/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzWlsDomainOnPV.java b/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzWlsDomainOnPV.java index 69c7a942523..e3d79e2c013 100644 --- a/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzWlsDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzWlsDomainOnPV.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.verrazzano.weblogic.kubernetes; @@ -56,6 +56,7 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; @@ -339,7 +340,7 @@ private File createWdtPropertyFile(String domainName) { // create a model property file File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile(wlsModelFilePrefix, ".properties"), + -> File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create WLS model properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java index 7e88055fcc0..a16d3a75a8a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java @@ -26,6 +26,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.getNextIntrospectVersion; @@ -166,7 +167,8 @@ private void createNewDynamicCluster() { logger.info("default channel port: {0}", defaultChannelPort); // create WLST property file - File wlstPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("wlst", "properties"), + File wlstPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("wlst", ".properties", new File(RESULTS_TEMPFILE)), "Creating WLST properties file failed"); Properties p1 = new Properties(); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index 0e8cf492e30..a2bce3a19b0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -66,6 +66,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_12213; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -203,7 +204,6 @@ public void initAll(@Namespaces(2) List namespaces) throws ApiException, // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); - //start two MySQL database instances String dbService1 = createMySQLDB("mysqldb-1", "root", "root123", domainNamespace, null); V1Pod pod = getPod(domainNamespace, null, "mysqldb-1"); @@ -218,7 +218,6 @@ public void initAll(@Namespaces(2) List namespaces) throws ApiException, dsUrl2 = "jdbc:mysql://" + dbService2 + "." + domainNamespace + ".svc:3306"; logger.info(dsUrl1); logger.info(dsUrl2); - // build the clusterview application Path distDir = buildApplication(Paths.get(APP_DIR, "clusterview"), @@ -237,7 +236,6 @@ public void initAll(@Namespaces(2) List namespaces) throws ApiException, createJdbcDataSource(dsName1, "root", "root123", dsUrl1); //deploy application to view server configuration deployApplication(clusterName + "," + adminServerName); - } /** @@ -944,7 +942,7 @@ private void createDomain() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile("domain", ".properties"), + -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", uniquePath); @@ -1125,7 +1123,7 @@ private void createJdbcDataSource(String dsName, String user, String password, S // 12.2.1.3 - com.mysql.jdbc.Driver // 12.2.1.4 and above - com.mysql.cj.jdbc.Driver // create a temporary WebLogic domain property file - File domainPropertiesFile = File.createTempFile("domain", "properties"); + File domainPropertiesFile = File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)); Properties p = new Properties(); p.setProperty("admin_host", adminServerPodName); p.setProperty("admin_port", Integer.toString(defaultChannelPort)); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java index 22ed2b69c2a..76dc9c10fed 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java @@ -29,6 +29,7 @@ import static oracle.weblogic.kubernetes.TestConstants.HTTP_PROXY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.NO_PROXY; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_VERSION; @@ -287,7 +288,7 @@ private File createWdtPropertyFile(int t3ChannelPort) { // create a model property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile(fmwModelFilePrefix, "properties"), + File.createTempFile(fmwModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create FMW model properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index 5efdddd36cb..26e5e981f8c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -41,6 +41,7 @@ import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.impl.primitive.Image.getImageEnvVar; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; @@ -179,7 +180,7 @@ void testFmwDomainInPvUsingWlst() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); //get ENV variable from the image diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java index e27e59105df..5c40727fe70 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java @@ -46,6 +46,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.createConfigMap; @@ -792,7 +793,7 @@ private File createWdtPropertyFile(String domainUid, String rcuSchemaPrefix) { // create a model property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile(fmwModelFilePrefix, ".properties"), + File.createTempFile(fmwModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create FMW model properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index 280f2ca1493..53bf12844ca 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -49,6 +49,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; @@ -766,7 +767,7 @@ private File createWdtPropertyFile(String domainName, String rcuSchemaPrefix) { // create a model property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile(fmwModelFilePrefix, ".properties"), + File.createTempFile(fmwModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create FMW model properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java index e71e35afe9b..32858aa3ac1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java @@ -40,6 +40,7 @@ import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.impl.primitive.Image.getImageEnvVar; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; @@ -326,7 +327,7 @@ private File createWlstPropertyFile(int t3ChannelPort) { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index cae3b976f1b..d334a92408e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -78,6 +78,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; @@ -305,7 +306,8 @@ void testDomainIntrospectVersionNotRolling() { logger.info("change the cluster1 size to 3 and verify the introspector runs and updates the domain status"); // create a temporary WebLogic WLST property file - File wlstPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("wlst", "properties"), + File wlstPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("wlst", ".properties", new File(RESULTS_TEMPFILE)), "Creating WLST properties file failed"); Properties p1 = new Properties(); p1.setProperty("admin_host", adminServerPodName); @@ -552,9 +554,9 @@ void testCredentialChange() { getPodCreationTime(introDomainNamespace, cluster1ManagedServerPodNamePrefix + i)); } - // create a temporary WebLogic WLST property file - File wlstPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("wlst", "properties"), + File wlstPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("wlst", ".properties", new File(RESULTS_TEMPFILE)), "Creating WLST properties file failed"); Properties p = new Properties(); p.setProperty("admin_host", adminServerPodName); @@ -677,7 +679,8 @@ void testCreateNewCluster() { = getServicePort(introDomainNamespace, getExternalServicePodName(adminServerPodName), "default"); // create a temporary WebLogic WLST property file - File wlstPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("wlst", "properties"), + File wlstPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("wlst", ".properties", new File(RESULTS_TEMPFILE)), "Creating WLST properties file failed"); Properties p = new Properties(); p.setProperty("admin_host", adminServerPodName); @@ -1143,7 +1146,7 @@ private static void createDomain() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", uniquePath); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java index cee503830af..cbef7c27ecf 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java @@ -52,6 +52,7 @@ import static oracle.weblogic.kubernetes.TestConstants.LOCALE_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.LOCALE_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -200,7 +201,8 @@ void testIstioDomainHomeInPv() throws UnknownHostException { createPVC(pvName, pvcName, domainUid, domainNamespace); // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("domain", "properties"), + File domainPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", "/shared/" + domainNamespace + "/domains"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index 256367a51fb..232a64c979c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -44,6 +44,7 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_ROLLING_TYPE; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -761,7 +762,7 @@ private static DomainResource createDomain(String domainNamespace, String domain int t3ChannelPort = getNextFreePort(); // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile("domain", "properties"), + -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", uniquePath); @@ -881,7 +882,8 @@ private void createNewCluster() { = getServicePort(domainNamespace3, adminServerPodName, "default"); // create a temporary WebLogic WLST property file - File wlstPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("wlst", "properties"), + File wlstPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("wlst", ".properties", new File(RESULTS_TEMPFILE)), "Creating WLST properties file failed"); Properties p = new Properties(); p.setProperty("admin_host", adminServerPodName); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index e8f8850c8b0..39e45edc674 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -40,6 +40,7 @@ import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; @@ -256,8 +257,8 @@ public void tearDownAll() throws ApiException { private static void createCertKeyFiles(String cn) { assertDoesNotThrow(() -> { - tlsKeyFile = Files.createTempFile("tls", ".key"); - tlsCertFile = Files.createTempFile("tls", ".crt"); + tlsKeyFile = Files.createTempFile(RESULTS_TEMPFILE_DIR, "tls", ".key"); + tlsCertFile = Files.createTempFile(RESULTS_TEMPFILE_DIR, "tls", ".crt"); String command = "openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout " + tlsKeyFile + " -out " + tlsCertFile + " -subj \"/CN=" + cn + "\""; logger.info("Executing command: {0}", command); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java index 30791cd607e..b47bf1916dd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java @@ -31,6 +31,7 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; @@ -219,8 +220,8 @@ public void tearDownAll() throws ApiException { private static void createCertKeyFiles(String cn) { assertDoesNotThrow(() -> { - tlsKeyFile = Files.createTempFile("tls", ".key"); - tlsCertFile = Files.createTempFile("tls", ".crt"); + tlsKeyFile = Files.createTempFile(RESULTS_TEMPFILE_DIR, "tls", ".key"); + tlsCertFile = Files.createTempFile(RESULTS_TEMPFILE_DIR, "tls", ".crt"); String command = "openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout " + tlsKeyFile + " -out " + tlsCertFile + " -subj \"/CN=" + cn + "\""; logger.info("Executing command: {0}", command); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java index 1d008ff7308..cd56ecae726 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java @@ -42,6 +42,7 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WLS_DOMAIN_TYPE; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -904,7 +905,7 @@ private static String createAndVerifyDomainImage() { } private static File createTempfile(String filename) throws IOException { - File tempFile = File.createTempFile(filename, ".txt"); + File tempFile = File.createTempFile(filename, ".txt", new File(RESULTS_TEMPFILE)); //deletes the file when VM terminates tempFile.deleteOnExit(); try (FileWriter fw = new FileWriter(tempFile)) { @@ -919,7 +920,7 @@ private static File createTempfile(String filename) throws IOException { * @throws IOException if can not create a file */ private File createScriptToKillServer() throws IOException { - File killServerScript = File.createTempFile("killserver", ".sh"); + File killServerScript = File.createTempFile("killserver", ".sh", new File(RESULTS_TEMPFILE)); //deletes the file when VM terminates killServerScript.deleteOnExit(); try (FileWriter fw = new FileWriter(killServerScript)) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java index 8110427e4a4..5360e5d34b5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java @@ -55,6 +55,7 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; @@ -96,7 +97,6 @@ * console and REST management interfaces are accessible thru appropriate channels. * Verify deployed customer applications are accessible in appropriate channels and ports. */ - @DisplayName("Test upgrade to 1412 image for a mii domain") @IntegrationTest @Tag("kind-parallel") @@ -1085,8 +1085,8 @@ private String createAdministrationIngressHostRouting(String domainUid, int admi private static void createCertKeyFiles(String cn) { assertDoesNotThrow(() -> { - tlsKeyFile = Files.createTempFile("tls", ".key"); - tlsCertFile = Files.createTempFile("tls", ".crt"); + tlsKeyFile = Files.createTempFile(RESULTS_TEMPFILE_DIR, "tls", ".key"); + tlsCertFile = Files.createTempFile(RESULTS_TEMPFILE_DIR, "tls", ".crt"); String command = "openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout " + tlsKeyFile + " -out " + tlsCertFile + " -subj \"/CN=" + cn + "\""; logger.info("Executing command: {0}", command); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java index 865cbd802c4..368aef174e3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java @@ -54,6 +54,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; @@ -771,7 +772,7 @@ private static String createAndVerifyDomainInImage() { p.setProperty("MYSQL_PWD", "wlpwd123"); // create a temporary WebLogic domain property file as a input for WDT model file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); assertDoesNotThrow(() -> p.store(new FileOutputStream(domainPropertiesFile), "WDT properties file"), diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java index c70343c50f9..9bed49957e0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -50,6 +50,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_GITHUB_CHART_REPO_URL; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.getOperatorContainerImageName; @@ -543,7 +544,7 @@ private File createWlstPropertyFile(int t3ChannelPort) { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java index 7f7a56b0ce2..38c0d1bdd7a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRecoveryDomainInPV.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -44,6 +44,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.scaleAllClustersInDomain; @@ -158,7 +159,7 @@ void testRecoveryDomainHomeInPv() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", "/shared/" + domainNamespace + "/domains"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index 69a96c12c68..17848275226 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -51,6 +51,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; @@ -383,7 +384,7 @@ private void createDomain() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile("domain", ".properties"), + -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", uniquePath); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 036b4a90fab..42185fd1d9b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -47,6 +47,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; @@ -162,7 +163,7 @@ void testAdminServerT3Channel() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", "properties"), + File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", "/shared/" + domainNamespace + "/domains"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java index f800fe8209d..4901587e8f3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -37,6 +37,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; @@ -205,7 +206,7 @@ private File createWdtPropertyFile(String domainName) { // create a model property file File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile(wlsModelFilePrefix, ".properties"), + File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create WLS model properties file"); // create the property file diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 48a6bf117b1..cd642610ed2 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -6,6 +6,7 @@ import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; import static oracle.weblogic.kubernetes.actions.TestActions.listNamespaces; @@ -194,6 +195,7 @@ public interface TestConstants { public static final String PV_ROOT = getNonEmptySystemProperty("wko.it.pv.root", RESULTS_BASE + "/pvroot"); public static final String RESULTS_ROOT = RESULTS_BASE + "/workdir"; + public static final String RESULTS_TEMPFILE = RESULTS_BASE + "/tmpfile"; // NGINX constants public static final String NGINX_REPO_URL = "https://kubernetes.github.io/ingress-nginx"; @@ -210,9 +212,11 @@ public interface TestConstants { public static final int NGINX_INGRESS_HTTPS_NODEPORT = 31443; public static final int NGINX_INGRESS_HTTP_HOSTPORT = 2180; public static final int NGINX_INGRESS_HTTPS_HOSTPORT = 2543; - + + public static final Path RESULTS_TEMPFILE_DIR = assertDoesNotThrow(() + -> Files.createDirectories(Paths.get(RESULTS_TEMPFILE))); public static final Path INGRESS_CLASS_FILE_NAME = assertDoesNotThrow(() - -> Files.createTempFile("ingressclass", ".name")); + -> Files.createTempFile(RESULTS_TEMPFILE_DIR, "ingressclass", ".name")); // Traefik constants public static final String TRAEFIK_REPO_URL = "https://helm.traefik.io/traefik"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index f552302cc50..d6b75138b9d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -69,6 +69,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; @@ -1701,7 +1702,8 @@ public static ExecResult accesseWLSViaWLSTUsingForwardedPort(String domainUid, } // create WLST property file - File wlstPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("wlst", "properties"), + File wlstPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("wlst", ".properties", new File(RESULTS_TEMPFILE)), "Creating WLST properties file failed"); String localhost = "localhost"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java index 22f87004c4b..60493b010fd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java @@ -34,6 +34,7 @@ import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.createConfigMap; @@ -77,7 +78,8 @@ public static void deployUsingWlst(String host, String port, String userName, createBaseRepoSecret(namespace); // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("domain", "properties"), + File domainPropertiesFile = + assertDoesNotThrow(() -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), "Creating domain properties file failed"); Properties p = new Properties(); p.setProperty("node_archive_path", MOUNT_POINT + archivePath.getFileName()); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java index 78dc344958a..2606ac78422 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -83,6 +83,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.NO_PROXY; import static oracle.weblogic.kubernetes.TestConstants.OKD; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_MODEL_PROPERTIES_FILE; import static oracle.weblogic.kubernetes.TestConstants.WDT_IMAGE_DOMAINHOME_BASE_DIR; @@ -687,7 +688,8 @@ public static DomainResource createDomainOnPvUsingWdt(String domainUid, // create a temporary WebLogic domain property file as a input for WDT model file - File domainPropertiesFile = assertDoesNotThrow(() -> createTempFile("domainonpv" + domainUid, "properties"), + File domainPropertiesFile = + assertDoesNotThrow(() -> createTempFile("domainonpv" + domainUid, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); @@ -815,7 +817,8 @@ public static DomainResource createDomainOnPvUsingWdt(String domainUid, // create a temporary WebLogic domain property file as a input for WDT model file - File domainPropertiesFile = assertDoesNotThrow(() -> createTempFile("domainonpv" + domainUid, "properties"), + File domainPropertiesFile = + assertDoesNotThrow(() -> createTempFile("domainonpv" + domainUid, ".properties", new File(RESULTS_TEMPFILE)), "Failed to create domain properties file"); Properties p = new Properties(); From 3bd0715668e91f9a436aeaaf990ade37485466b9 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Mon, 29 Apr 2024 20:14:14 +0000 Subject: [PATCH 049/356] Fix ItFmwDomainOnPVSample failed in release/4.2 --- .../wdt-artifacts/wdt-model-files/JRF-v1/model.10.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kubernetes/samples/scripts/create-weblogic-domain/wdt-artifacts/wdt-model-files/JRF-v1/model.10.yaml b/kubernetes/samples/scripts/create-weblogic-domain/wdt-artifacts/wdt-model-files/JRF-v1/model.10.yaml index b552494c117..b48ef38e9e3 100644 --- a/kubernetes/samples/scripts/create-weblogic-domain/wdt-artifacts/wdt-model-files/JRF-v1/model.10.yaml +++ b/kubernetes/samples/scripts/create-weblogic-domain/wdt-artifacts/wdt-model-files/JRF-v1/model.10.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. domainInfo: @@ -9,7 +9,7 @@ domainInfo: rcu_prefix: '@@SECRET:@@ENV:DOMAIN_UID@@-rcu-access:rcu_prefix@@' rcu_schema_password: '@@SECRET:@@ENV:DOMAIN_UID@@-rcu-access:rcu_schema_password@@' rcu_db_conn_string: '@@SECRET:@@ENV:DOMAIN_UID@@-rcu-access:rcu_db_conn_string@@' - rcu_db_user: sys + rcu_admin_user: sys rcu_admin_password: '@@SECRET:@@ENV:DOMAIN_UID@@-rcu-access:rcu_schema_password@@' topology: From 1447b3953092ce62b6cc4a73b39005516e16e27a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 30 Apr 2024 12:58:17 -0400 Subject: [PATCH 050/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index cb18d257baf..5478826d2f3 100644 --- a/pom.xml +++ b/pom.xml @@ -660,8 +660,8 @@ 3.4.1 3.3.2 3.13.0 - 3.1.1 - 3.1.1 + 3.1.2 + 3.1.2 3.4.1 3.3.1 3.12.1 @@ -671,9 +671,9 @@ 3.6.3 3.2.5 3.6.1 - 3.5.2 + 3.5.3 3.2.0 - 10.15.0 + 10.16.0 1.0 3.3.2 3.2.4 @@ -685,7 +685,7 @@ 1.7.0 1.4.0 1.4.0 - 1.16.1 + 1.17.0 1.7.3 0.1.0 2.9.0 From aa9db1b0f1e11174493be8c68c3b966409de1482 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 30 Apr 2024 14:17:23 -0400 Subject: [PATCH 051/356] Fix CheckStyle --- .../kubernetes/operator/helpers/CallBuilder.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/CallBuilder.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/CallBuilder.java index 58d3e377bea..ea6a063d92c 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/CallBuilder.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/CallBuilder.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2023, Oracle and/or its affiliates. +// Copyright (c) 2017, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -1696,10 +1696,6 @@ private Call listJobAsync( callback); } - private final CallFactory listJob = - (requestParams, usage, cont, callback) -> - wrap(listJobAsync(usage, requestParams.namespace, cont, callback)); - /** * Asynchronous step for listing jobs. * @@ -1712,6 +1708,10 @@ public Step listJobAsync(String namespace, ResponseStep responseStep) responseStep, new RequestParams("listJob", namespace, null, null, callParams), listJob); } + private final CallFactory listJob = + (requestParams, usage, cont, callback) -> + wrap(listJobAsync(usage, requestParams.namespace, cont, callback)); + private Call createJobAsync( ApiClient client, String namespace, V1Job body, ApiCallback callback) throws ApiException { From 00c4906ba0ef6605b1b8bd641ebf935033d3cb5c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 1 May 2024 14:46:41 +0000 Subject: [PATCH 052/356] Merge branch 'fix-getwlsversion-and-matchmodelunzip' into 'main' Fix how getWebLogicVersion is implemented and keep track of bin/lib contents... See merge request weblogic-cloud/weblogic-kubernetes-operator!4682 (cherry picked from commit 0bcc038ea9a4202caa833712308e49f1182352be) 3e2fdf8b Fix how getWebLogicVersion is implemented and keep track of bin/lib contents... --- .../resources/scripts/introspectDomain.py | 7 ++++- .../main/resources/scripts/modelInImage.sh | 26 ++++++++++++++--- operator/src/main/resources/scripts/utils.sh | 28 ++++++++----------- .../src/main/resources/scripts/wlversion.py | 8 ++++++ 4 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 operator/src/main/resources/scripts/wlversion.py diff --git a/operator/src/main/resources/scripts/introspectDomain.py b/operator/src/main/resources/scripts/introspectDomain.py index c37292783d1..d1e20d0936a 100644 --- a/operator/src/main/resources/scripts/introspectDomain.py +++ b/operator/src/main/resources/scripts/introspectDomain.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # # ------------ @@ -165,6 +165,9 @@ def open(self): # Check environment variable that skip leasing validation self.SKIP_LEASING_VALIDATIONS = self.getEnvOrDef('SKIP_LEASING_VALIDATIONS', "False") + # Extracted list for bin lib during domain creation + self.DOMAIN_LIB_BIN_LIST = self.INTROSPECT_HOME + "/binliblist" + # maintain a list of errors that we include in topology.yaml on completion, if any self.errors = [] @@ -1969,6 +1972,8 @@ def introspect(self): trace("cfgmap write domain wdt version") if os.path.exists('/tmp/domain_wdt_version'): MII_IntrospectCMFileGenerator(self.env, self.env.MII_DOMAIN_WDT_VERSION, '/tmp/domain_wdt_version').generate() + if os.path.exists('/tmp/binlibdir.txt'): + MII_IntrospectCMFileGenerator(self.env, self.env.DOMAIN_LIB_BIN_LIST, '/tmp/binlibdir.txt').generate() if self.isFromModelAndJRFDomain() or self.isInitializeDomainJRFOnPV(): trace("cfgmap write JRF wallet") diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 2d3123d9f8d..f8536d7df91 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # # This script contains the all the function of model in image @@ -28,7 +28,7 @@ LOCAL_PRIM_DOMAIN_TAR="/tmp/prim_domain.tar" NEW_MERGED_MODEL="/tmp/new_merged_model.json" WDT_CONFIGMAP_ROOT="/weblogic-operator/wdt-config-map" RUNTIME_ENCRYPTION_SECRET_PASSWORD="/weblogic-operator/model-runtime-secret/password" - +DOMAIN_BIN_LIB_LIST="/weblogic-operator/introspector/binliblist" # we export the opss password file location because it's also used by introspectDomain.py export OPSS_KEY_PASSPHRASE="/weblogic-operator/opss-walletkey-secret/walletPassword" OPSS_KEY_B64EWALLET="/weblogic-operator/opss-walletfile-secret/walletFile" @@ -1065,6 +1065,8 @@ wdtUpdateModelDomain() { # local MII_PASSPHRASE=$(cat ${RUNTIME_ENCRYPTION_SECRET_PASSWORD}) + captureBinLibAdded + gzip ${DOMAIN_HOME}/wlsdeploy/domain_model.json || exitOrLoop base64 ${DOMAIN_HOME}/wlsdeploy/domain_model.json.gz > ${DOMAIN_HOME}/wlsdeploy/domain_model.json.b64 || exitOrLoop encrypt_decrypt_model "encrypt" ${DOMAIN_HOME}/wlsdeploy/domain_model.json.b64 ${MII_PASSPHRASE} \ @@ -1077,6 +1079,12 @@ wdtUpdateModelDomain() { trace "Exiting wdtUpdateModelDomain" } +captureBinLibAdded() { + local BINLIBDIR_NAME="/tmp/binlibdir.txt" + find $DOMAIN_HOME/bin -maxdepth 1 -type f | sed "s|$DOMAIN_HOME/bin|wlsdeploy/domainBin|g" > $BINLIBDIR_NAME + find $DOMAIN_HOME/lib -maxdepth 1 -type f | sed "s|$DOMAIN_HOME/bin|wlsdeploy/domainLibraries|g" >> $BINLIBDIR_NAME +} + wdtHandleOnlineUpdate() { trace "Entering wdtHandleOnlineUpdate" @@ -1305,7 +1313,11 @@ restoreAppAndLibs() { # expand the archive domain libraries to the domain lib, 11 is caution when zip entry doesn't exists cd ${DOMAIN_HOME}/lib || exitOrLoop - unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainLibraries/* + if [ -f $DOMAIN_BIN_LIB_LIST ] ; then + unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} $(awk '{print $0}' <<< $(grep "wlsdeploy/domainLibraries" $DOMAIN_BIN_LIB_LIST)) + else + unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainLibraries/* + fi ret=$? if [ $ret -ne 0 ] && [ $ret -ne 11 ] ; then trace SEVERE "Domain Source Type is FromModel, error in extracting domainLibraries " \ @@ -1316,7 +1328,13 @@ restoreAppAndLibs() { # expand the domain bin, in update case user may only update a file in the domainBin archive, 11 is caution when # zip entry doesn't exists cd ${DOMAIN_HOME}/bin || exitOrLoop - unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainBin/* + if [ -f $DOMAIN_BIN_LIB_LIST ] ; then + unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} $(awk '{print $0}' <<< $(grep "wlsdeploy/domainBin" $DOMAIN_BIN_LIB_LIST)) + else + unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainBin/* + fi + + #unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainBin/* ret=$? if [ $ret -ne 0 ] && [ $ret -ne 11 ] ; then trace SEVERE "Domain Source Type is FromModel, error in extracting domainBin " \ diff --git a/operator/src/main/resources/scripts/utils.sh b/operator/src/main/resources/scripts/utils.sh index 13979f62ffd..3f7b5c2292c 100644 --- a/operator/src/main/resources/scripts/utils.sh +++ b/operator/src/main/resources/scripts/utils.sh @@ -1,4 +1,5 @@ -# Copyright (c) 2017, 2023, Oracle and/or its affiliates. +#!/bin/sh +# Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. set -o pipefail @@ -494,21 +495,16 @@ hasWebLogicPatches() } # getWebLogicVersion -# parse wl version from install inventory # - if we can't get a version number then we return # a high dummy version number that's sufficient # to pass version checks "9999.9999.9999.9999" -# - we parse the install inventory as this is far faster than -# using opatch or weblogic.version getWebLogicVersion() { - local reg_file=$ORACLE_HOME/inventory/registry.xml + $ORACLE_HOME/oracle_common/common/bin/wlst.sh /weblogic-operator/scripts/wlversion.py > /dev/null 2>&1 - [ ! -f $reg_file ] && echo "9999.9999.9999.9999" && return - - # The following grep captures both "WebLogic Server" and "WebLogic Server for FMW" - local wlver="`grep 'name="WebLogic Server.*version=' $reg_file \ - | sed 's/.*version="\([0-9.]*\)".*/\1/g'`" + if [ $? -eq 0 ] ; then + wlver=$(cat /tmp/wlsversion.txt) + fi echo ${wlver:-"9999.9999.9999.9999"} } @@ -538,9 +534,9 @@ getMajorVersion() # checkWebLogicVersion # check if the WL version is supported by the Operator # - skip check if SKIP_WL_VERSION_CHECK = "true" -# - log an error if WL version < 12.2.1.3 -# - log an error if WL version == 12.2.1.3 && patch 29135930 is missing -# - you can override the required 12.2.1.3 patches by exporting +# - log an error if WL version < 12.2.1.3.0 +# - log an error if WL version == 12.2.1.3.0 && patch 29135930 is missing +# - you can override the required 12.2.1.3.0 patches by exporting # global WL12213REQUIREDPATCHES to an empty string or to other # patch number(s) # - return 1 if logged an error @@ -549,11 +545,11 @@ checkWebLogicVersion() { [ "$SKIP_WL_VERSION_CHECK" = "true" ] && return 0 local cur_wl_ver="`getWebLogicVersion`" - local exp_wl_ver="12.2.1.3" + local exp_wl_ver="12.2.1.3.0" local exp_wl_12213_patches="${WL12213REQUIREDPATCHES:-"29135930"}" - if versionEQ "$cur_wl_ver" "12.2.1.3" ; then + if versionEQ "$cur_wl_ver" "12.2.1.3.0" ; then if ! hasWebLogicPatches $exp_wl_12213_patches ; then - trace SEVERE "The Operator requires that WebLogic version '12.2.1.3' have patch '$exp_wl_12213_patches'. To bypass this check, set env var SKIP_WL_VERSION_CHECK to 'true'." + trace SEVERE "The Operator requires that WebLogic version '12.2.1.3.0' have patch '$exp_wl_12213_patches'. To bypass this check, set env var SKIP_WL_VERSION_CHECK to 'true'." return 1 fi fi diff --git a/operator/src/main/resources/scripts/wlversion.py b/operator/src/main/resources/scripts/wlversion.py new file mode 100644 index 00000000000..f813a831653 --- /dev/null +++ b/operator/src/main/resources/scripts/wlversion.py @@ -0,0 +1,8 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +import weblogic.version as version_helper +fh = open('/tmp/wlsversion.txt', 'w') +fh.write(version_helper.getReleaseBuildVersion()) +fh.close() +exit() From 5e1bc79799c992d6c4b6f00f428ad2c2af60a71d Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 3 May 2024 19:03:53 +0000 Subject: [PATCH 053/356] Fix for nightly failures in release 4.2 --- Jenkinsfile.podman | 129 ++++++++------- .../weblogic/kubernetes/ItDedicatedMode.java | 8 +- .../kubernetes/ItExternalLbTunneling.java | 18 +-- .../kubernetes/ItExternalNodePortService.java | 10 +- ...tHorizontalPodAutoscalerCustomMetrics.java | 14 +- .../kubernetes/ItIstioMonitoringExporter.java | 8 +- .../kubernetes/ItLBTwoDomainsNginx.java | 14 +- .../ItMiiDomainUpgradeToSecureMode.java | 42 +++-- .../ItMonitoringExporterMetricsFiltering.java | 63 ++++++-- .../ItMonitoringExporterSamples.java | 153 ++++++++++++++---- .../ItMonitoringExporterSideCar.java | 53 ++++-- .../ItMonitoringExporterWebApp.java | 82 +++++++--- .../weblogic/kubernetes/ItRemoteConsole.java | 42 ++--- .../ItUsabilityOperatorHelmChart.java | 65 ++++---- .../oracle/weblogic/kubernetes/ItWseeSSO.java | 12 +- .../weblogic/kubernetes/TestConstants.java | 118 +++++++++----- .../kubernetes/actions/TestActions.java | 25 ++- .../kubernetes/actions/impl/Domain.java | 86 +++++++++- .../kubernetes/utils/CommonMiiTestUtils.java | 2 +- .../kubernetes/utils/CommonTestUtils.java | 80 +++++++-- .../weblogic/kubernetes/utils/DbUtils.java | 59 ++++++- .../kubernetes/utils/MonitoringUtils.java | 55 ++++++- .../traefik/traefik-ingress-rules-onprem.yaml | 2 +- .../traefik-ingress-rules-remoteconsole.yaml | 2 +- .../traefik-ingress-rules-stickysession.yaml | 4 +- .../traefik/traefik-ingress-rules-tcp.yaml | 4 +- .../traefik/traefik-ingress-rules.yaml | 20 +-- .../traefik.tls.tunneling.template.yaml | 6 +- .../tunneling/traefik.tunneling.template.yaml | 2 +- .../charts/traefik/samples/host-routing.yaml | 4 +- .../charts/traefik/samples/path-routing.yaml | 8 +- .../samples/charts/traefik/samples/tls.yaml | 4 +- .../samples/quick-start/ingress-route.yaml | 4 +- ...k-ingress-sample-domain1-admin-server.yaml | 2 +- ...ress-sample-domain1-cluster-cluster-1.yaml | 2 +- ...ress-sample-domain2-cluster-cluster-1.yaml | 2 +- .../stage-and-create-ingresses.sh | 4 +- .../stage-and-create-ingresses.sh | 4 +- 38 files changed, 882 insertions(+), 330 deletions(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 68c04564459..59237672340 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -390,66 +390,86 @@ nodes: - role: worker image: ${kind_image} extraPortMappings: - - containerPort: 30880 - hostPort: 2080 - protocol: TCP - - containerPort: 30443 - hostPort: 2443 - protocol: TCP - - containerPort: 31880 - hostPort: 2180 - protocol: TCP - - containerPort: 31443 - hostPort: 2543 - - containerPort: 30881 - hostPort: 2081 - protocol: TCP - - containerPort: 30444 - hostPort: 2444 - protocol: TCP + - containerPort: 30511 + hostPort: 1511 - containerPort: 32480 hostPort: 2480 - containerPort: 32490 - hostPort: 2490 - - containerPort: 32156 + hostPort: 2490 + - containerPort: 30080 + hostPort: 2080 + - containerPort: 30443 + hostPort: 2043 + - containerPort: 30180 + hostPort: 2090 + - containerPort: 30143 + hostPort: 2053 + - containerPort: 31000 + hostPort: 2100 + - containerPort: 31004 + hostPort: 2104 + - containerPort: 31008 + hostPort: 2108 + - containerPort: 31012 + hostPort: 2112 + - containerPort: 31016 + hostPort: 2116 + - containerPort: 31020 + hostPort: 2120 + - containerPort: 31024 + hostPort: 2124 + - containerPort: 31028 + hostPort: 2128 + - containerPort: 31032 + hostPort: 2132 + - containerPort: 31036 + hostPort: 2136 + - containerPort: 31040 + hostPort: 2140 + - containerPort: 31044 + hostPort: 2144 + - containerPort: 31048 + hostPort: 2148 + - containerPort: 31052 + hostPort: 2152 + - containerPort: 31056 hostPort: 2156 - - containerPort: 30511 - hostPort: 1511 - - containerPort: 32159 + - containerPort: 31060 hostPort: 2160 - - containerPort: 32169 + - containerPort: 31064 + hostPort: 2164 + - containerPort: 31068 + hostPort: 2168 + - containerPort: 31072 hostPort: 2172 - protocol: TCP - - containerPort: 32170 - hostPort: 2173 - protocol: TCP - - containerPort: 32189 - hostPort: 2182 - protocol: TCP - - containerPort: 32185 - hostPort: 2175 - protocol: TCP - - containerPort: 32143 - hostPort: 2143 - protocol: TCP - - containerPort: 32343 - hostPort: 2343 - protocol: TCP - - containerPort: 32331 - hostPort: 2331 - protocol: TCP - - containerPort: 31781 - hostPort: 2781 - protocol: TCP - - containerPort: 31744 - hostPort: 2782 - protocol: TCP - - containerPort: 31785 - hostPort: 2785 - protocol: TCP - - containerPort: 31746 - hostPort: 2786 - protocol: TCP + - containerPort: 31076 + hostPort: 2176 + - containerPort: 31080 + hostPort: 2180 + - containerPort: 31084 + hostPort: 2184 + - containerPort: 31088 + hostPort: 2188 + - containerPort: 31092 + hostPort: 2192 + - containerPort: 31096 + hostPort: 2196 + - containerPort: 31100 + hostPort: 2200 + - containerPort: 31104 + hostPort: 2204 + - containerPort: 31108 + hostPort: 2208 + - containerPort: 31112 + hostPort: 2212 + - containerPort: 31116 + hostPort: 2216 + - containerPort: 31120 + hostPort: 2220 + - containerPort: 31124 + hostPort: 2224 + - containerPort: 31128 + hostPort: 2228 extraMounts: - hostPath: ${pv_root} containerPath: ${pv_root} @@ -549,6 +569,7 @@ EOF echo "${WORKSPACE}/.mvn/maven.config contents:" cat "${WORKSPACE}/.mvn/maven.config" cp "${WORKSPACE}/.mvn/maven.config" "${result_root}" + kubectl describe node kind-worker ''' withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { withCredentials([ diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java index a4a63e5add0..703c5beba3e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java @@ -31,8 +31,8 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; -import static oracle.weblogic.kubernetes.TestConstants.IT_DEDICATED_MODE_CONAINERPORT; -import static oracle.weblogic.kubernetes.TestConstants.IT_DEDICATED_MODE_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_DEDICATEDMODE_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_DEDICATEDMODE_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; @@ -86,7 +86,7 @@ class ItDedicatedMode { domainUid + "-" + ADMIN_SERVER_NAME_BASE; private final String managedServerPodPrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; - private static int externalRestHttpsPort = IT_DEDICATED_MODE_CONAINERPORT; + private static int externalRestHttpsPort = IT_DEDICATEDMODE_NODEPORT; // operator constants private static HelmParams opHelmParams; @@ -200,7 +200,7 @@ void testDedicatedModeSameNamespace() { if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { externalRestHttpshost = "localhost"; - externalRestHttpsPort = IT_DEDICATED_MODE_HOSTPORT; + externalRestHttpsPort = IT_DEDICATEDMODE_HOSTPORT; logger.info("Running in podman using Operator hostport {0}:{1}", externalRestHttpshost, externalRestHttpsPort); } else { logger.info("externalRestHttpsPort {0}", externalRestHttpsPort); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java index 7cf91ecdf05..a589f05d70e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java @@ -47,10 +47,10 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; -import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLB_TUNNELING_HTTPS_CONAINERPORT; -import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLB_TUNNELING_HTTPS_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLB_TUNNELING_HTTP_CONAINERPORT; -import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLB_TUNNELING_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLBTUNNELING_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLBTUNNELING_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLBTUNNELING_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALLBTUNNELING_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOSTNAME; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; @@ -219,7 +219,7 @@ public static void initAll(@Namespaces(3) List namespaces) throws Unknow if (!OKD) { logger.info("Installing Traefik controller using helm"); traefikHelmParams = installAndVerifyTraefik(traefikNamespace, - IT_EXTERNALLB_TUNNELING_HTTP_CONAINERPORT, IT_EXTERNALLB_TUNNELING_HTTPS_CONAINERPORT).getHelmParams(); + IT_EXTERNALLBTUNNELING_HTTP_NODEPORT, IT_EXTERNALLBTUNNELING_HTTPS_NODEPORT).getHelmParams(); } // Create SSL certificate and key using openSSL with SAN extension @@ -296,10 +296,10 @@ void testExternalRmiAccessThruTraefik() { String service = TRAEFIK_RELEASE_NAME + "-" + traefikNamespace.substring(3); logger.info("TRAEFIK_SERVICE {0} in {1}", service, traefikNamespace); - int httpTunnelingPort = IT_EXTERNALLB_TUNNELING_HTTP_CONAINERPORT; + int httpTunnelingPort = IT_EXTERNALLBTUNNELING_HTTP_NODEPORT; if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - httpTunnelingPort = IT_EXTERNALLB_TUNNELING_HTTP_HOSTPORT; + httpTunnelingPort = IT_EXTERNALLBTUNNELING_HTTP_HOSTPORT; } assertNotEquals(-1, httpTunnelingPort, "Could not get the Traefik HttpTunnelingPort service node port"); @@ -357,10 +357,10 @@ void testExternalRmiAccessThruTraefikHttpsTunneling() { String service = TRAEFIK_RELEASE_NAME + "-" + traefikNamespace.substring(3); logger.info("TRAEFIK_SERVICE {0} in {1}", service, traefikNamespace); - int httpsTunnelingPort = IT_EXTERNALLB_TUNNELING_HTTPS_CONAINERPORT; + int httpsTunnelingPort = IT_EXTERNALLBTUNNELING_HTTPS_NODEPORT; if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - httpsTunnelingPort = IT_EXTERNALLB_TUNNELING_HTTPS_HOSTPORT; + httpsTunnelingPort = IT_EXTERNALLBTUNNELING_HTTPS_HOSTPORT; } assertNotEquals(-1, httpsTunnelingPort, "Could not get the Traefik HttpsTunnelingPort service node port"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalNodePortService.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalNodePortService.java index 7c145fc342a..f359f536f12 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalNodePortService.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalNodePortService.java @@ -42,8 +42,8 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; -import static oracle.weblogic.kubernetes.TestConstants.ITEXTERNALNODEPORTSERVICE_CONAINERPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITEXTERNALNODEPORTSERVICE_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALNODEPORTSERVICE_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_EXTERNALNODEPORTSERVICE_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOSTNAME; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; @@ -159,7 +159,7 @@ public static void initAll(@Namespaces(2) List namespaces) { configTemplateMap.put("INGRESS_HOST", K8S_NODEPORT_HOST); if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - configTemplateMap.put("FREE_PORT", String.valueOf(ITEXTERNALNODEPORTSERVICE_CONAINERPORT)); + configTemplateMap.put("FREE_PORT", String.valueOf(IT_EXTERNALNODEPORTSERVICE_NODEPORT)); } else { nextFreePort = getNextFreePort(); configTemplateMap.put("FREE_PORT", String.valueOf(nextFreePort)); @@ -230,7 +230,7 @@ void testExternalRmiAccessThruNodePortService() { templateMap.put("CLUSTER", clusterName); if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - templateMap.put("FREE_PORT", String.valueOf(ITEXTERNALNODEPORTSERVICE_CONAINERPORT)); + templateMap.put("FREE_PORT", String.valueOf(IT_EXTERNALNODEPORTSERVICE_NODEPORT)); } else { templateMap.put("INGRESS_HOST", K8S_NODEPORT_HOST); templateMap.put("FREE_PORT", String.valueOf(nextFreePort)); @@ -265,7 +265,7 @@ void testExternalRmiAccessThruNodePortService() { String hostAndPort; if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostAndPort = getHostAndPort(clusterSvcRouteHost + ":80", ITEXTERNALNODEPORTSERVICE_HOSTPORT); + hostAndPort = getHostAndPort(clusterSvcRouteHost + ":80", IT_EXTERNALNODEPORTSERVICE_HOSTPORT); } else { hostAndPort = getHostAndPort(clusterSvcRouteHost + ":80", httpTunnelingPort); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java index 599e8868d13..4aaec28c08f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java @@ -49,9 +49,9 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; -import static oracle.weblogic.kubernetes.TestConstants.ITHPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITHPACUSTOMNGINX_INGRESS_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_HPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_HPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_HPACUSTOMNGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; @@ -222,8 +222,8 @@ public static void initAll(@Namespaces(4) List namespaces) { ); // install and verify NGINX - nginxHelmParams = installAndVerifyNginx(nginxNamespace, ITHPACUSTOMNGINX_INGRESS_HTTP_NODEPORT, - ITHPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, (OKE_CLUSTER ? null : "NodePort")); + nginxHelmParams = installAndVerifyNginx(nginxNamespace, IT_HPACUSTOMNGINX_INGRESS_HTTP_NODEPORT, + IT_HPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, (OKE_CLUSTER ? null : "NodePort")); String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; logger.info("NGINX service name: {0}", nginxServiceName); @@ -238,7 +238,7 @@ public static void initAll(@Namespaces(4) List namespaces) { } catch (UnknownHostException ex) { logger.severe(ex.getLocalizedMessage()); } - nodeportshttp = ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; + nodeportshttp = IT_HPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; } // create cluster resouce with limits and requests in serverPod @@ -314,7 +314,7 @@ void testHPAWithCustomMetrics() { } catch (UnknownHostException ex) { logger.severe(ex.getLocalizedMessage()); } - nodeportshttp = ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; + nodeportshttp = IT_HPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT; hostPort = host + ":" + nodeportshttp; } String curlCmd = diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java index 9d40da548a3..1293a37e8d7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java @@ -28,8 +28,8 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ISTIO_HTTP_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_CONAINERPORT; -import static oracle.weblogic.kubernetes.TestConstants.IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ISTIOMONITORINGEXPORTER_PROMETHEUS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ISTIOMONITORINGEXPORTER_PROMETHEUS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; @@ -146,7 +146,7 @@ public static void initAll(@Namespaces(3) List namespaces) { // install and verify operator installAndVerifyOperator(opNamespace, domain1Namespace, domain2Namespace); - prometheusPort = IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_CONAINERPORT; + prometheusPort = IT_ISTIOMONITORINGEXPORTER_PROMETHEUS_HTTP_NODEPORT; } /** @@ -232,7 +232,7 @@ private void deployPrometheusAndVerify(String domainNamespace, String domainUid, if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !OCNE) { try { hostPortPrometheus = InetAddress.getLocalHost().getHostAddress() - + ":" + IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_HOSTPORT; + + ":" + IT_ISTIOMONITORINGEXPORTER_PROMETHEUS_HTTP_HOSTPORT; } catch (UnknownHostException ex) { logger.severe(ex.getLocalizedMessage()); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index 39e45edc674..36e4c59aab6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -32,10 +32,10 @@ import org.junit.jupiter.api.TestMethodOrder; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; -import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_LBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_LBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; @@ -485,7 +485,7 @@ private static void createNginxTLSPathRoutingForTwoDomains() { private static int getNginxLbNodePort(String channelName) { if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { return channelName.equals("https") - ? ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT : ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT; + ? IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT : IT_LBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT; } else { String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; return getServiceNodePort(nginxNamespace, nginxServiceName, channelName); @@ -519,8 +519,8 @@ private static NginxParams installNginxLB() { nodePortValue = "NodePort"; } - NginxParams params = installAndVerifyNginx(nginxNamespace, ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT, - ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, nodePortValue); + NginxParams params = installAndVerifyNginx(nginxNamespace, IT_LBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT, + IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, nodePortValue); return params; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java index 5360e5d34b5..bc27193c795 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java @@ -4,6 +4,8 @@ package oracle.weblogic.kubernetes; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -52,6 +54,10 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; @@ -76,6 +82,7 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.verifyRollingRestartOccurred; import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createDomainResourceWithAuxiliaryImage; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; @@ -212,7 +219,7 @@ void afterEach() { @Test @DisplayName("Test upgrade from 1411 container image to " + "1412 container image with production off and secure mode off") - void testUpgrade1411to1412ProdOff() { + void testUpgrade1411to1412ProdOff() throws UnknownHostException { domainNamespace = namespaces.get(2); domainUid = "testdomain1"; adminServerPodName = domainUid + "-" + adminServerName; @@ -295,7 +302,7 @@ void testUpgrade1411to1412ProdOff() { */ @Test @DisplayName("Test upgrade from 1411 to 1412 with production on and secure mode off") - void testUpgrade1411to1412ProdOnSecOff() { + void testUpgrade1411to1412ProdOnSecOff() throws UnknownHostException { domainNamespace = namespaces.get(3); domainUid = "testdomain2"; adminServerPodName = domainUid + "-" + adminServerName; @@ -378,7 +385,7 @@ void testUpgrade1411to1412ProdOnSecOff() { */ @Test @DisplayName("Test upgrade from 1411 to 1412 with production on and secure mode on") - void testUpgrade1411to1412ProdOnSecOn() { + void testUpgrade1411to1412ProdOnSecOn() throws UnknownHostException { domainNamespace = namespaces.get(4); domainUid = "testdomain3"; adminServerPodName = domainUid + "-" + adminServerName; @@ -464,7 +471,7 @@ void testUpgrade1411to1412ProdOnSecOn() { */ @Test @DisplayName("Test upgrade from 1411 to 1412 with production on and secure mode not configured") - void testUpgrade1411to1412ProdOnSecNotConfigured() { + void testUpgrade1411to1412ProdOnSecNotConfigured() throws UnknownHostException { domainNamespace = namespaces.get(5); domainUid = "testdomain4"; adminServerPodName = domainUid + "-" + adminServerName; @@ -553,7 +560,7 @@ void testUpgrade1411to1412ProdOnSecNotConfigured() { @Test @DisplayName("Test upgrade from 12214 container image to " + "1412 container image with production off and secure mode off") - void testUpgrade12214to1412ProdOff() { + void testUpgrade12214to1412ProdOff() throws UnknownHostException { domainNamespace = namespaces.get(6); domainUid = "testdomain5"; adminServerPodName = domainUid + "-" + adminServerName; @@ -638,7 +645,7 @@ void testUpgrade12214to1412ProdOff() { */ @Test @DisplayName("Test upgrade from 12214 to 1412 with production on and administration port enabled") - void testUpgrade12214to1412ProdOn() { + void testUpgrade12214to1412ProdOn() throws UnknownHostException { domainNamespace = namespaces.get(7); domainUid = "testdomain6"; adminServerPodName = domainUid + "-" + adminServerName; @@ -865,7 +872,9 @@ private Map getPodsWithTimeStamps(String domainNamespace, String do private static void installNginx() { // install and verify Nginx ingress controller logger.info("Installing Nginx controller using helm"); - nginxParams = installAndVerifyNginx(ingressNamespace, 0, 0); + nginxParams = installAndVerifyNginx(ingressNamespace, + IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_NODEPORT, + IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_NODEPORT); } /** @@ -1096,6 +1105,14 @@ private static void createCertKeyFiles(String cn) { private static int getNginxLbNodePort(String channelName) { String nginxServiceName = nginxParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + if (channelName.equals("https")) { + return IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_HOSTPORT; + } else { + return IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_HOSTPORT; + } + } return getServiceNodePort(ingressNamespace, nginxServiceName, channelName); } @@ -1106,19 +1123,20 @@ private void verifyAppServerAccess(boolean isTLS, String pathLocation, String content, boolean useCredentials, - String... hostName) { + String... hostName) throws UnknownHostException { StringBuffer url = new StringBuffer(); String hostAndPort; if (hostName != null && hostName.length > 0) { hostAndPort = OKE_CLUSTER_PRIVATEIP ? hostName[0] : hostName[0] + ":" + lbNodePort; } else { - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } + String host = formatIPv6Host(K8S_NODEPORT_HOST); hostAndPort = host + ":" + lbNodePort; } + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + hostAndPort = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + lbNodePort; + } if (isTLS) { url.append("https://"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java index 118447c23a9..4c21fb21ced 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java @@ -4,6 +4,8 @@ package oracle.weblogic.kubernetes; import java.io.IOException; +import java.net.InetAddress; +import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Base64; @@ -36,11 +38,20 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.GRAFANA_CHART_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.INGRESS_CLASS_FILE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERMF_ALERT_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deleteImage; @@ -135,7 +146,7 @@ class ItMonitoringExporterMetricsFiltering { */ @BeforeAll - public void initAll(@Namespaces(4) List namespaces) { + public void initAll(@Namespaces(4) List namespaces) throws IOException { logger = getLogger(); monitoringExporterDir = Paths.get(RESULTS_ROOT, @@ -174,6 +185,9 @@ public void initAll(@Namespaces(4) List namespaces) { miiImage = MonitoringUtils.createAndVerifyMiiImage(monitoringExporterAppDir, modelList, STICKYSESS_APP_NAME, SESSMIGR_APP_NAME, MONEXP_IMAGE_NAME); host = formatIPv6Host(K8S_NODEPORT_HOST); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } if (!OKD) { // install and verify Traefik @@ -511,15 +525,27 @@ private void installPrometheusGrafana(String promChartVersion, final String prometheusRegexValue = String.format("regex: %s;%s", domainNS, domainUid); if (promHelmParams == null) { cleanupPromGrafanaClusterRoles(prometheusReleaseName, grafanaReleaseName); - String promHelmValuesFileDir = Paths.get(RESULTS_ROOT,this.getClass().getSimpleName(), + String promHelmValuesFileDir = Paths.get(RESULTS_ROOT, this.getClass().getSimpleName(), "prometheus" + releaseSuffix).toString(); - promHelmParams = installAndVerifyPrometheus(releaseSuffix, - monitoringNS, - promChartVersion, - prometheusRegexValue, promHelmValuesFileDir); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir, null, + IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_NODEPORT, IT_MONITORINGEXPORTERMF_ALERT_HTTP_NODEPORT); + } else { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir); + } assertNotNull(promHelmParams, " Failed to install prometheus"); nodeportPrometheus = promHelmParams.getNodePortServer(); String host = formatIPv6Host(K8S_NODEPORT_HOST); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + nodeportPrometheus = IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_HOSTPORT; + } hostPortPrometheus = host + ":" + nodeportPrometheus; if (OKE_CLUSTER_PRIVATEIP) { @@ -542,7 +568,8 @@ private void installPrometheusGrafana(String promChartVersion, // create ingress rules with non-tls host routing, tls host routing and path routing for Traefik createIngressPathRouting(monitoringNS, "/api", - prometheusReleaseName + "-server", 80, ingressClassName); + prometheusReleaseName + "-server", 80, ingressClassName, prometheusReleaseName + + "." + monitoringNS); } @@ -683,7 +710,8 @@ private void replaceConfiguration(String configFile, String checkMetricsPromethe Thread.sleep(20 * 1000); // "heap_free_current{name="managed-server1"}[15s]" search for results for last 15secs checkMetricsViaPrometheus(checkMetricsPrometheusString, - expectedValue, hostPortPrometheus); + expectedValue, hostPortPrometheus, prometheusReleaseName + + "." + monitoringNS); } } @@ -733,18 +761,25 @@ private void appendConfiguration(String configFile) throws Exception { } - private static void installTraefikIngressController() { + private static void installTraefikIngressController() throws IOException { // install and verify Traefik logger.info("Installing Traefik controller using helm"); - traefikParams = installAndVerifyTraefik(traefikNamespace, 0, 0); - traefikHelmParams = traefikParams.getHelmParams(); - ingressClassName = traefikParams.getIngressClassName(); + if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + traefikParams = installAndVerifyTraefik(traefikNamespace, 0, 0); + traefikHelmParams = traefikParams.getHelmParams(); + ingressClassName = traefikParams.getIngressClassName(); + } else { + ingressClassName = Files.readString(INGRESS_CLASS_FILE_NAME); + } } private int getTraefikLbNodePort(boolean isHttps) { + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + return isHttps ? TRAEFIK_INGRESS_HTTPS_HOSTPORT : TRAEFIK_INGRESS_HTTP_HOSTPORT; + } logger.info("Getting web node port for Traefik loadbalancer {0}", traefikHelmParams.getReleaseName()); - return assertDoesNotThrow(() -> - getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), isHttps ? "websecure" : "web"), + return assertDoesNotThrow(() + -> getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), isHttps ? "websecure" : "web"), "Getting web node port for Traefik loadbalancer failed"); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java index 368aef174e3..90ebdc7b745 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java @@ -1,11 +1,17 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -38,6 +44,8 @@ import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.assertions.impl.Deployment; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecResult; +import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; @@ -48,7 +56,15 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.GRAFANA_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_ALERT_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_PROMETHEUS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSAMPLES_PROMETHEUS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; @@ -58,11 +74,14 @@ import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WLS; import static oracle.weblogic.kubernetes.actions.TestActions.deleteImage; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; +import static oracle.weblogic.kubernetes.actions.TestActions.getPod; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.shutdownDomain; import static oracle.weblogic.kubernetes.actions.TestActions.uninstallNginx; @@ -77,6 +96,11 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.scaleAndVerifyCluster; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; +import static oracle.weblogic.kubernetes.utils.DbUtils.createSqlFileInPod; +import static oracle.weblogic.kubernetes.utils.DbUtils.runMysqlInsidePod; +import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; +import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; import static oracle.weblogic.kubernetes.utils.ImageUtils.createImageAndPushToRepo; import static oracle.weblogic.kubernetes.utils.ImageUtils.createImageAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; @@ -94,6 +118,7 @@ import static oracle.weblogic.kubernetes.utils.MonitoringUtils.uninstallPrometheusGrafana; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.verifyMonExpAppAccess; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.verifyMonExpAppAccessThroughNginx; +import static oracle.weblogic.kubernetes.utils.MySQLDBUtils.createMySQLDB; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPvAndPvc; @@ -121,14 +146,15 @@ class ItMonitoringExporterSamples { private static int managedServersCount = 2; private static String domain1Namespace = null; private static String domain2Namespace = null; - private static String domain1Uid = "monexp-domain-1"; - private static String domain2Uid = "monexp-domain-2"; + private static String domain1Uid = "monexpdomain1"; + private static String domain2Uid = "monexpdomain2"; private static NginxParams nginxHelmParams = null; private static int nodeportshttp = 0; private static int nodeportshttps = 0; private static List ingressHost1List = null; private static List ingressHost2List = null; + private static String dbService = null; private static String monitoringNS = null; private static String webhookNS = null; @@ -226,13 +252,24 @@ public static void initAll(@Namespaces(6) List namespaces) { SESSMIGR_APP_NAME, MONEXP_IMAGE_NAME); if (!OKD) { // install and verify NGINX - nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + if (OKE_CLUSTER_PRIVATEIP) { + nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + } else { + nginxHelmParams = installAndVerifyNginx(nginxNamespace, + IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTP_NODEPORT, + IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTPS_NODEPORT); + } String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; ingressIP = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST; logger.info("NGINX service name: {0}", nginxServiceName); - nodeportshttp = getServiceNodePort(nginxNamespace, nginxServiceName, "http"); - nodeportshttps = getServiceNodePort(nginxNamespace, nginxServiceName, "https"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + nodeportshttp = IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTP_HOSTPORT; + nodeportshttps = IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTPS_HOSTPORT; + } else { + nodeportshttp = getServiceNodePort(nginxNamespace, nginxServiceName, "http"); + nodeportshttps = getServiceNodePort(nginxNamespace, nginxServiceName, "https"); + } logger.info("NGINX http node port: {0}", nodeportshttp); logger.info("NGINX https node port: {0}", nodeportshttps); } @@ -253,6 +290,15 @@ public static void initAll(@Namespaces(6) List namespaces) { assertDoesNotThrow(() -> createPvAndPvc(grafanaReleaseName, monitoringNS, labels, className)); cleanupPromGrafanaClusterRoles(prometheusReleaseName, grafanaReleaseName); } + //start MySQL database instance + assertDoesNotThrow(() -> { + dbService = createMySQLDB("mysql", "root", "root123", domain2Namespace, null); + assertNotNull(dbService, "Failed to create database"); + V1Pod pod = getPod(domain2Namespace, null, "mysql"); + createFileInPod(pod.getMetadata().getName(), domain2Namespace, "root123"); + runMysqlInsidePod(pod.getMetadata().getName(), domain2Namespace, "root123", "/tmp/grant.sql"); + runMysqlInsidePod(pod.getMetadata().getName(), domain2Namespace, "root123", "/tmp/create.sql"); + }); } /** @@ -302,7 +348,9 @@ void testEndToEndViaChart() throws Exception { logger.info("verify metrics via prometheus"); String testappPrometheusSearchKey = "wls_servlet_invocation_total_count%7Bapp%3D%22test-webapp%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(testappPrometheusSearchKey, "test-webapp", hostPortPrometheus); + checkMetricsViaPrometheus(testappPrometheusSearchKey, "test-webapp", hostPortPrometheus, + prometheusReleaseName + + "." + monitoringNS); logger.info("fire alert by scaling down"); fireAlert(); @@ -319,7 +367,9 @@ void testEndToEndViaChart() throws Exception { editPrometheusCM(oldRegex, newRegex, monitoringNS, prometheusReleaseName + "-server"); String sessionAppPrometheusSearchKey = "wls_servlet_invocation_total_count%7Bapp%3D%22myear%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus); + checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus, + prometheusReleaseName + + "." + monitoringNS); } } finally { shutdownDomain(domain1Uid, domain1Namespace); @@ -350,7 +400,7 @@ private void fireAlert() throws ApiException { assertNotNull(pod, "Can't find running webhook pod"); logger.info("Wait for the webhook to fire alert and check webhook log file in {0} namespace ", webhookNS); - testUntil( + testUntil(withLongRetryPolicy, assertDoesNotThrow(() -> searchPodLogForKey(pod, "Some WLS cluster has only one running server for more than 1 minutes"), "webhook failed to fire alert"), @@ -366,22 +416,32 @@ private void installPrometheusGrafana(String promChartVersion, String grafanaChartVersion, String domainNS, String domainUid - ) throws ApiException { + ) throws ApiException, UnknownHostException { final String prometheusRegexValue = String.format("regex: %s;%s", domainNS, domainUid); if (promHelmParams == null) { cleanupPromGrafanaClusterRoles(prometheusReleaseName,grafanaReleaseName); String promHelmValuesFileDir = Paths.get(RESULTS_ROOT, this.getClass().getSimpleName(), "prometheus" + releaseSuffix).toString(); - promHelmParams = installAndVerifyPrometheus(releaseSuffix, - monitoringNS, - promChartVersion, - prometheusRegexValue, - promHelmValuesFileDir, - webhookNS); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir, webhookNS, + IT_MONITORINGEXPORTERSAMPLES_PROMETHEUS_HTTP_NODEPORT, IT_MONITORINGEXPORTERSAMPLES_ALERT_HTTP_NODEPORT); + } else { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir, webhookNS); + } assertNotNull(promHelmParams, " Failed to install prometheus"); nodeportPrometheus = promHelmParams.getNodePortServer(); - prometheusDomainRegexValue = prometheusRegexValue; String host = formatIPv6Host(K8S_NODEPORT_HOST); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + nodeportPrometheus = IT_MONITORINGEXPORTERSAMPLES_PROMETHEUS_HTTP_HOSTPORT; + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } + prometheusDomainRegexValue = prometheusRegexValue; hostPortPrometheus = host + ":" + nodeportPrometheus; if (OKE_CLUSTER_PRIVATEIP) { @@ -393,7 +453,8 @@ private void installPrometheusGrafana(String promChartVersion, } String ingressClassName = nginxHelmParams.getIngressClassName(); createIngressPathRouting(monitoringNS, "/api", - prometheusReleaseName + "-server", 80, ingressClassName); + prometheusReleaseName + "-server", 80, ingressClassName, prometheusReleaseName + + "." + monitoringNS); } //if prometheus already installed change CM for specified domain if (!prometheusRegexValue.equals(prometheusDomainRegexValue)) { @@ -450,18 +511,30 @@ private void installCoordinator(String namespace) throws ApiException { labelMap, "coordsecret"), "Failed to start coordinator"); } + private static void createFileInPod(String podName, String namespace, String password) throws IOException { + + ExecResult result = assertDoesNotThrow(() -> exec(new String("hostname -i"), true)); + String ip = result.stdout(); + String sqlCommand = "select user();\n" + + "SELECT host, user FROM mysql.user;\n" + + "CREATE USER 'root'@'%' IDENTIFIED BY '" + password + "';\n" + + "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;\n" + + "CREATE USER 'root'@'" + ip + "' IDENTIFIED BY '" + password + "';\n" + + "GRANT ALL PRIVILEGES ON *.* TO 'root'@'" + ip + "' WITH GRANT OPTION;\n" + + "SELECT host, user FROM mysql.user;"; + String fileName = "grant.sql"; + createSqlFileInPod(podName, namespace, sqlCommand, fileName); + fileName = "create.sql"; + sqlCommand = + "CREATE DATABASE " + domain2Uid + ";\n" + + "CREATE USER 'wluser1' IDENTIFIED BY 'wlpwd123';\n" + + "GRANT ALL ON " + domain2Uid + ".* TO 'wluser1';"; + createSqlFileInPod(podName, namespace, sqlCommand, fileName); + } + @AfterAll public void tearDownAll() { - // uninstall NGINX release - logger.info("Uninstalling NGINX"); - if (nginxHelmParams != null) { - assertThat(uninstallNginx(nginxHelmParams.getHelmParams())) - .as("Test uninstallNginx1 returns true") - .withFailMessage("uninstallNginx() did not return true") - .isTrue(); - } - // delete mii domain images created for parameterized test if (miiImage != null) { deleteImage(miiImage); @@ -483,6 +556,12 @@ public void tearDownAll() { deleteNamespace(monitoringNS); uninstallDeploymentService(webhookDepl, webhookService); uninstallDeploymentService(coordinatorDepl, coordinatorService); + if (nginxHelmParams != null) { + assertThat(uninstallNginx(nginxHelmParams.getHelmParams())) + .as("Test uninstallNginx1 returns true") + .withFailMessage("uninstallNginx() did not return true") + .isTrue(); + } // delete coordinator and webhook images if (webhookImage != null) { deleteImage(webhookImage); @@ -781,8 +860,24 @@ private static String createAndVerifyDomainInImage() { final List propertyList = Collections.singletonList(domainPropertiesFile.getPath()); // build the model file list - final List modelList = Collections.singletonList(monitoringExporterEndToEndDir - + MONEXP_WDT_FILE); + logger.info("create a staging location for the scripts"); + Path fileTemp = Paths.get(RESULTS_ROOT, "ItMonitoringExporterSamples", "temp","sampleTopologyTemp"); + assertDoesNotThrow(() -> FileUtils.deleteDirectory(fileTemp.toFile()),"Failed to delete temp dir for topology"); + + assertDoesNotThrow(() -> Files.createDirectories(fileTemp), "Failed to create temp dir for topology"); + logger.info("copy the " + MONEXP_WDT_FILE + " to staging location"); + Path srcPromFile = Paths.get(monitoringExporterEndToEndDir, MONEXP_WDT_FILE); + Path targetPromFile = Paths.get(fileTemp.toString(), "simple-topology.yaml"); + assertDoesNotThrow(() -> Files.copy(srcPromFile, targetPromFile, + StandardCopyOption.REPLACE_EXISTING)," Failed to copy files"); + String dbURL = dbService + "." + domain2Namespace + ".svc"; + assertDoesNotThrow(() -> { + replaceStringInFile(targetPromFile.toString(), + "mysql.default.svc.cluster.local", + dbURL); + }); + + final List modelList = Collections.singletonList(targetPromFile.toString()); wdtImage = createImageAndVerify(MONEXP_IMAGE_NAME, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java index 301652450bd..64bb8efeed0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java @@ -4,6 +4,7 @@ package oracle.weblogic.kubernetes; import java.io.IOException; +import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; @@ -35,13 +36,19 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_FAILED_TYPE; import static oracle.weblogic.kubernetes.TestConstants.GRAFANA_CHART_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSIDECAR_ALERT_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSIDECAR_PROMETHEUS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERSIDECAR_PROMETHEUS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; @@ -251,7 +258,8 @@ void testSideCarBasicFunctionality() throws Exception { String sessionAppPrometheusSearchKey = "wls_servlet_invocation_total_count%7Bapp%3D%22myear%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus); + checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus, + prometheusReleaseName + "." + monitoringNS); } DomainResource domain = getDomainCustomResource(domain3Uid, domain3Namespace); String monexpConfig = domain.getSpec().getMonitoringExporter().toString(); @@ -374,7 +382,8 @@ private void changeMonitoringExporterSideCarConfig(String configYamlFile, String checkPodReadyAndServiceExists(managedServerPodName + "1", domainUid, domainNamespace); checkPodReadyAndServiceExists(managedServerPodName + "2", domainUid, domainNamespace); if (!OKD) { - checkMetricsViaPrometheus(promSearchString, expectedVal, hostPortPrometheus); + checkMetricsViaPrometheus(promSearchString, expectedVal, hostPortPrometheus, + prometheusReleaseName + "." + monitoringNS); } } @@ -401,9 +410,11 @@ void testSideCarBasicFunctionalityTwoClusters() throws Exception { // "heap_free_current{name="managed-server1"}[15s]" search for results for last 15secs checkMetricsViaPrometheus("heap_free_current%7Bname%3D%22" + cluster1Name + "-managed-server1%22%7D%5B15s%5D", - cluster1Name + "-managed-server1",hostPortPrometheus); + cluster1Name + "-managed-server1",hostPortPrometheus, + prometheusReleaseName + "." + monitoringNS); checkMetricsViaPrometheus("heap_free_current%7Bname%3D%22" + cluster2Name + "-managed-server2%22%7D%5B15s%5D", - cluster2Name + "-managed-server2",hostPortPrometheus); + cluster2Name + "-managed-server2",hostPortPrometheus, + prometheusReleaseName + "." + monitoringNS); } finally { shutdownDomain(domain1Uid, domain1Namespace); } @@ -434,7 +445,8 @@ void testSideCarBasicFunctionalityWithSSL() throws Exception { String sessionAppPrometheusSearchKey = "wls_servlet_invocation_total_count%7Bapp%3D%22myear%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus); + checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus, + prometheusReleaseName + "." + monitoringNS); } DomainResource domain = getDomainCustomResource(domain2Uid,domain2Namespace); @@ -464,11 +476,18 @@ private void installPrometheusGrafana(String promChartVersion, cleanupPromGrafanaClusterRoles(prometheusReleaseName,grafanaReleaseName); String promHelmValuesFileDir = Paths.get(RESULTS_ROOT, this.getClass().getSimpleName(), "prometheus" + releaseSuffix).toString(); - promHelmParams = installAndVerifyPrometheus(releaseSuffix, - monitoringNS, - promChartVersion, - prometheusRegexValue, - promHelmValuesFileDir); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir, null, + IT_MONITORINGEXPORTERSIDECAR_PROMETHEUS_HTTP_NODEPORT, IT_MONITORINGEXPORTERSIDECAR_ALERT_HTTP_NODEPORT); + } else { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir); + } assertNotNull(promHelmParams, " Failed to install prometheus"); String command1 = KUBERNETES_CLI + " get svc -n " + monitoringNS; assertDoesNotThrow(() -> ExecCommand.exec(command1,true)); @@ -476,13 +495,19 @@ private void installPrometheusGrafana(String promChartVersion, assertDoesNotThrow(() -> ExecCommand.exec(command2, true)); createIngressPathRouting(monitoringNS, "/api", - prometheusReleaseName + "-server", 80, ingressClassName); + prometheusReleaseName + "-server", 80, ingressClassName, prometheusReleaseName + + "." + monitoringNS); if (!OKE_CLUSTER_PRIVATEIP) { nodeportPrometheus = promHelmParams.getNodePortServer(); String host = formatIPv6Host(K8S_NODEPORT_HOST); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + nodeportPrometheus = IT_MONITORINGEXPORTERSIDECAR_PROMETHEUS_HTTP_HOSTPORT; + logger.info("Running in podman Debug 1 : {0}", hostPortPrometheus); + } hostPortPrometheus = host + ":" + nodeportPrometheus; - + logger.info("Running in podman Debug 2 : {0}", hostPortPrometheus); } prometheusDomainRegexValue = prometheusRegexValue; } @@ -498,6 +523,7 @@ private void installPrometheusGrafana(String promChartVersion, assertDoesNotThrow(() -> ExecCommand.exec(command1,true)); String command2 = KUBERNETES_CLI + " describe svc -n " + monitoringNS; assertDoesNotThrow(() -> ExecCommand.exec(command2,true)); + logger.info("Running in podman Debug 3 : {0}", hostPortPrometheus); if (OKD) { hostPortPrometheus = createRouteForOKD("prometheus" + releaseSuffix @@ -510,8 +536,10 @@ private void installPrometheusGrafana(String promChartVersion, monitoringNS, grafanaHelmValuesFileDir, grafanaChartVersion); + logger.info("Running in podman Debug 4 : {0}", hostPortPrometheus); assertNotNull(grafanaHelmParams, "Grafana failed to install"); String host = formatIPv6Host(K8S_NODEPORT_HOST); + logger.info("Running in podman Debug 5 : {0}", hostPortPrometheus); String hostPortGrafana = host + ":" + grafanaHelmParams.getNodePort(); if (OKE_CLUSTER_PRIVATEIP) { @@ -521,6 +549,7 @@ private void installPrometheusGrafana(String promChartVersion, hostPortGrafana = createRouteForOKD(grafanaReleaseName, monitoringNS) + ":" + grafanaHelmParams.getNodePort(); } } + logger.info("Running in podman Debug 6 : {0}", hostPortPrometheus); logger.info("Grafana is running"); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java index 6aedc9b24fd..076de002905 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java @@ -1,9 +1,11 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Base64; @@ -43,11 +45,21 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.GRAFANA_CHART_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_ALERT_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_PROMETHEUS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERWEBAPP_PROMETHEUS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deleteImage; @@ -151,7 +163,7 @@ class ItMonitoringExporterWebApp { */ @BeforeAll - public static void initAll(@Namespaces(6) List namespaces) { + public static void initAll(@Namespaces(6) List namespaces) throws UnknownHostException { logger = getLogger(); monitoringExporterDir = Paths.get(RESULTS_ROOT, @@ -193,16 +205,30 @@ public static void initAll(@Namespaces(6) List namespaces) { logger.info("create and verify WebLogic domain image using model in image with model files"); miiImage = MonitoringUtils.createAndVerifyMiiImage(monitoringExporterAppDir, MODEL_DIR + "/" + MONEXP_MODEL_FILE, SESSMIGR_APP_NAME, MONEXP_IMAGE_NAME); + String host = formatIPv6Host(K8S_NODEPORT_HOST); if (!OKD) { // install and verify NGINX - nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + if (!OKE_CLUSTER_PRIVATEIP) { + nginxHelmParams = installAndVerifyNginx(nginxNamespace, + IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTP_NODEPORT, + IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTPS_NODEPORT); + } else { + nginxHelmParams = installAndVerifyNginx(nginxNamespace, + 0,0); + } String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; ingressIP = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST; logger.info("NGINX service name: {0}", nginxServiceName); - nodeportshttp = getServiceNodePort(nginxNamespace, nginxServiceName, "http"); - nodeportshttps = getServiceNodePort(nginxNamespace, nginxServiceName, "https"); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + nodeportshttp = IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTP_HOSTPORT; + nodeportshttps = IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTPS_HOSTPORT; + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } else { + nodeportshttp = getServiceNodePort(nginxNamespace, nginxServiceName, "http"); + nodeportshttps = getServiceNodePort(nginxNamespace, nginxServiceName, "https"); + } } logger.info("NGINX http node port: {0}", nodeportshttp); logger.info("NGINX https node port: {0}", nodeportshttps); @@ -212,8 +238,6 @@ public static void initAll(@Namespaces(6) List namespaces) { clusterNames.add(cluster1Name); clusterNames.add(cluster2Name); - String host = formatIPv6Host(K8S_NODEPORT_HOST); - exporterUrl = String.format("http://%s:%s/wls-exporter/", host, nodeportshttp); if (OKE_CLUSTER_PRIVATEIP) { exporterUrl = String.format("http://%s/wls-exporter/", ingressIP); @@ -425,14 +449,27 @@ private void installPrometheusGrafana(String promChartVersion, cleanupPromGrafanaClusterRoles(prometheusReleaseName, grafanaReleaseName); String promHelmValuesFileDir = Paths.get(RESULTS_ROOT, this.getClass().getSimpleName(), "prometheus" + releaseSuffix).toString(); - promHelmParams = installAndVerifyPrometheus(releaseSuffix, - monitoringNS, - promChartVersion, - prometheusRegexValue, promHelmValuesFileDir); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir, null, + IT_MONITORINGEXPORTERWEBAPP_PROMETHEUS_HTTP_NODEPORT, IT_MONITORINGEXPORTERWEBAPP_ALERT_HTTP_NODEPORT); + } else { + promHelmParams = installAndVerifyPrometheus(releaseSuffix, + monitoringNS, + promChartVersion, + prometheusRegexValue, promHelmValuesFileDir); + } assertNotNull(promHelmParams, " Failed to install prometheus"); prometheusDomainRegexValue = prometheusRegexValue; - nodeportPrometheus = promHelmParams.getNodePortServer(); String host = formatIPv6Host(K8S_NODEPORT_HOST); + nodeportPrometheus = promHelmParams.getNodePortServer(); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + nodeportPrometheus = IT_MONITORINGEXPORTERWEBAPP_PROMETHEUS_HTTP_HOSTPORT; + host = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } + hostPortPrometheus = host + ":" + nodeportPrometheus; if (OKE_CLUSTER_PRIVATEIP) { hostPortPrometheus = ingressIP; @@ -443,7 +480,9 @@ private void installPrometheusGrafana(String promChartVersion, } String ingressClassName = nginxHelmParams.getIngressClassName(); createIngressPathRouting(monitoringNS, "/api", - prometheusReleaseName + "-server", 80, ingressClassName); + prometheusReleaseName + "-server", 80, ingressClassName, + prometheusReleaseName + + "." + monitoringNS); } //if prometheus already installed change CM for specified domain if (!prometheusRegexValue.equals(prometheusDomainRegexValue)) { @@ -626,7 +665,8 @@ private void replaceConfiguration() throws Exception { // "heap_free_current{name="managed-server1"}[15s]" search for results for last 15secs checkMetricsViaPrometheus("heap_free_current%7Bname%3D%22" + cluster1Name + "-managed-server1%22%7D%5B15s%5D", - cluster1Name + "-managed-server1", hostPortPrometheus); + cluster1Name + "-managed-server1", hostPortPrometheus, prometheusReleaseName + + "." + monitoringNS); } } @@ -648,7 +688,9 @@ private void appendConfiguration() throws Exception { if (!OKD) { String sessionAppPrometheusSearchKey = "wls_servlet_invocation_total_count%7Bapp%3D%22myear%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus); + checkMetricsViaPrometheus(sessionAppPrometheusSearchKey, "sessmigr", hostPortPrometheus, + prometheusReleaseName + + "." + monitoringNS); } } @@ -789,7 +831,8 @@ private void replaceMetricsNameSnakeCaseFalseConfiguration() throws Exception { assertFalse(page.asNormalizedText().contains("metricsNameSnakeCase")); if (!OKD) { String searchKey = "wls_servlet_executionTimeAverage%7Bapp%3D%22myear%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(searchKey, "sessmigr", hostPortPrometheus); + checkMetricsViaPrometheus(searchKey, "sessmigr", hostPortPrometheus, prometheusReleaseName + + "." + monitoringNS); } } @@ -811,7 +854,8 @@ private void replaceMetricsNoRestPortConfiguration() throws Exception { String prometheusSearchKey1 = "heap_free_current"; - checkMetricsViaPrometheus(prometheusSearchKey1, "managed-server1", hostPortPrometheus); + checkMetricsViaPrometheus(prometheusSearchKey1, "managed-server1", hostPortPrometheus, prometheusReleaseName + + "." + monitoringNS); } } @@ -829,7 +873,9 @@ private void replaceMetricsDomainQualifierTrueConfiguration() throws Exception { assertTrue(page.asNormalizedText().contains("domainQualifier")); if (!OKD) { String searchKey = "wls_servlet_executionTimeAverage%7Bapp%3D%22myear%22%7D%5B15s%5D"; - checkMetricsViaPrometheus(searchKey, "\"domain\":\"wls-monexp-domain-1" + "\"", hostPortPrometheus); + checkMetricsViaPrometheus(searchKey, "\"domain\":\"wls-monexp-domain-1" + "\"", hostPortPrometheus, + prometheusReleaseName + + "." + monitoringNS); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java index 61c691e8981..684df8c5770 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java @@ -38,16 +38,17 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.IT_REMOTECONSOLENGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_REMOTECONSOLENGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_REMOTECONSOLENGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTPS_NODEPORT; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTP_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.NGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.OKD; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; @@ -65,6 +66,7 @@ import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReturnedCode; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createSSLenabledMiiDomainAndVerify; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createIngressAndRetryIfFail; @@ -93,7 +95,6 @@ @DisabledOnSlimImage @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("okd-wls-mrg") @Tag("oke-parallel") class ItRemoteConsole { @@ -381,19 +382,15 @@ private static void createNginxIngressPathRoutingRules() throws UnknownHostExcep logger.info("nginxServiceName is {0}", nginxServiceName); if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { - nginxNodePort = NGINX_INGRESS_HTTP_HOSTPORT; + nginxNodePort = IT_REMOTECONSOLENGINX_INGRESS_HTTP_HOSTPORT; } else if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { nginxNodePort = assertDoesNotThrow(() -> getServiceNodePort(nginxNamespace, nginxServiceName, "http"), "Getting Nginx loadbalancer service node port failed"); } logger.info("nginxNodePort is {0}", nginxNodePort); - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } - if (KIND_CLUSTER - && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + String host = formatIPv6Host(K8S_NODEPORT_HOST); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { host = InetAddress.getLocalHost().getHostAddress(); } @@ -407,16 +404,14 @@ private static void createNginxIngressPathRoutingRules() throws UnknownHostExcep logger.info("Executing curl command {0}", curlCmd); assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); } - + private static void installTraefikIngressController() { - - if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { - logger.info("Installing Traefik controller using helm"); - traefikHelmParams = installAndVerifyTraefik(traefikNamespace, 0, 0).getHelmParams(); + if (!OKD || (KIND_CLUSTER + && WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT))) { + traefikHelmParams = installAndVerifyTraefik(traefikNamespace, 0, 0, (OKE_CLUSTER ? null : "NodePort")) + .getHelmParams(); } - createTraefikIngressRoutingRules(domainNamespace); - } private static void installNgnixIngressController() throws UnknownHostException { @@ -426,9 +421,9 @@ private static void installNgnixIngressController() throws UnknownHostException nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); } else if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { logger.info("Installing Ngnix controller using http_nodeport {0}, https_nodeport {1}", - NGINX_INGRESS_HTTP_NODEPORT, NGINX_INGRESS_HTTPS_NODEPORT); + IT_REMOTECONSOLENGINX_INGRESS_HTTP_NODEPORT, IT_REMOTECONSOLENGINX_INGRESS_HTTPS_NODEPORT); nginxHelmParams = installAndVerifyNginx(nginxNamespace, - NGINX_INGRESS_HTTP_NODEPORT, NGINX_INGRESS_HTTPS_NODEPORT); + IT_REMOTECONSOLENGINX_INGRESS_HTTP_NODEPORT, IT_REMOTECONSOLENGINX_INGRESS_HTTPS_NODEPORT); } createNginxIngressPathRoutingRules(); @@ -457,11 +452,8 @@ private static void verifyRemoteConsoleConnectionThroughLB(int nodePortOfLB, Str } private static String getLBhostAndPort(int nodePortOfLB, String type) { - String host = K8S_NODEPORT_HOST; String hostAndPort = null; - if (host.contains(":")) { - host = "[" + host + "]"; - } + String host = formatIPv6Host(K8S_NODEPORT_HOST); String ingressServiceName = null; String traefikNamespace = null; if (type.equalsIgnoreCase("traefik")) { @@ -512,6 +504,4 @@ public static boolean shutdownWlsRemoteConsole() { return true; } - - } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java index a7095cdf693..3b05b65b3ca 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java @@ -3,6 +3,9 @@ package oracle.weblogic.kubernetes; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.net.http.HttpResponse; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -35,6 +38,7 @@ import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; +import oracle.weblogic.kubernetes.utils.OracleHttpClient; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -58,6 +62,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_SERVICE_NAME; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WLS_DOMAIN_TYPE; import static oracle.weblogic.kubernetes.actions.TestActions.createServiceAccount; import static oracle.weblogic.kubernetes.actions.TestActions.deleteDomainCustomResource; @@ -77,15 +82,15 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.operatorIsReady; import static oracle.weblogic.kubernetes.assertions.TestAssertions.operatorRestServiceRunning; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podStateNotChanged; +import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceDoesNotExist; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; -import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; @@ -145,6 +150,8 @@ class ItUsabilityOperatorHelmChart { private String adminSvcExtRouteHost = null; private static LoggingFacade logger = null; + private static String hostHeader; + private static int adminPort = 7001; /** * Get namespaces for operator, domain. @@ -230,7 +237,7 @@ public void tearDownAll() { @Test @DisplayName("install operator helm chart and domain, " + " then uninstall operator helm chart and verify the domain is still running") - void testDeleteOperatorButNotDomain() { + void testDeleteOperatorButNotDomain() throws UnknownHostException { try { // install and verify operator logger.info("Installing and verifying operator"); @@ -863,7 +870,7 @@ void testTwoDomainsInSameNameSpaceOnOperator() { checkPodExists(managedServerPodName2, domain5Uid, domain4Namespace), "operator failed to manage domain2, scaling was not succeeded"); - logger.info("Domain4 scaled to 3 servers"); + logger.info("Domain5 scaled to 3 servers"); assertDoesNotThrow(() -> TestActions.scaleClusterWithScalingActionScript(clusterName, domain4Uid, domain4Namespace, @@ -912,17 +919,16 @@ void testTwoDomainsInSameNameSpaceOnOperator() { } private boolean createVerifyDomain(String domainNamespace, String domainUid) { - // create and verify the domain logger.info("Creating and verifying model in image domain"); - createAndVerifyMiiDomain(domainNamespace, domainUid); + assertDoesNotThrow(() -> createAndVerifyMiiDomain(domainNamespace, domainUid)); return true; } /** * Create a model in image domain and verify the domain pods are ready. */ - private void createAndVerifyMiiDomain(String domainNamespace, String domainUid) { + private void createAndVerifyMiiDomain(String domainNamespace, String domainUid) throws UnknownHostException { // get the pre-built image created by IntegrationTestWatcher String miiImage = MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG; @@ -974,7 +980,7 @@ private void createAndVerifyMiiDomain(String domainNamespace, String domainUid) .adminService(new oracle.weblogic.domain.AdminService() .addChannelsItem(new oracle.weblogic.domain.Channel() .channelName("default") - .nodePort(getNextFreePort())))) + .nodePort(0)))) .configuration(new Configuration() .introspectorJobActiveDeadlineSeconds(280L) .model(new Model() @@ -1033,6 +1039,12 @@ private void createAndVerifyMiiDomain(String domainNamespace, String domainUid) managedServerPodName, domainNamespace); checkServiceExists(managedServerPodName, domainNamespace); } + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + hostHeader = createIngressHostRouting(domainNamespace, domainUid, ADMIN_SERVER_NAME_BASE, adminPort); + assertDoesNotThrow(() -> verifyAdminServerRESTAccess(InetAddress.getLocalHost().getHostAddress(), + TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); + } //check the access to managed server mbean via rest api checkManagedServerConfiguration(domainNamespace, domainUid); } @@ -1256,35 +1268,28 @@ private void cleanUpSA(String namespace) { * @param managedServer name of the managed server * @returns true if MBEAN is found otherwise false **/ - private boolean checkManagedServerConfiguration(String domainNamespace, String domainUid) { + private boolean checkManagedServerConfiguration(String domainNamespace, String domainUid) + throws UnknownHostException { ExecResult result; String adminServerPodName = domainUid + adminServerPrefix; String managedServer = "managed-server1"; int adminServiceNodePort = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); String hostAndPort = getHostAndPort(adminSvcExtRouteHost, adminServiceNodePort); - StringBuilder checkCluster = new StringBuilder("status=$(curl -g --user ") - .append(ADMIN_USERNAME_DEFAULT) - .append(":") - .append(ADMIN_PASSWORD_DEFAULT) - .append(" ") - .append("http://") - .append(hostAndPort) - .append("/management/tenant-monitoring/servers/") - .append(managedServer) - .append(" --silent --show-error ") - .append(" -o /dev/null") - .append(" -w %{http_code});") - .append("echo ${status}"); - logger.info("checkManagedServerConfiguration: curl command {0}", new String(checkCluster)); - try { - result = exec(new String(checkCluster), true); - } catch (Exception ex) { - logger.info("Exception in checkManagedServerConfiguration() {0}", ex); - return false; + + Map httpHeaders = new HashMap<>(); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + httpHeaders.put("host", domainNamespace + "." + domainUid + "." + ADMIN_SERVER_NAME_BASE); + hostAndPort = InetAddress.getLocalHost().getHostAddress() + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT; } - logger.info("checkManagedServerConfiguration: curl command returned {0}", result.toString()); - return result.stdout().equals("200"); + httpHeaders.put("Authorization", ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT); + String url = "http://" + hostAndPort + "/management/tenant-monitoring/servers/" + managedServer; + testUntil(() -> { + HttpResponse response = OracleHttpClient.get(url, httpHeaders, true); + return (response.statusCode() == 200 && response.body().contains("HEALTH_OK")); + }, logger, "WebLogic managed server to be running {0}", managedServer); + return true; } private void cleanUpDomainSecrets(String domainNamespace) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java index f3451575300..d23f9b56d5f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java @@ -42,9 +42,9 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; -import static oracle.weblogic.kubernetes.TestConstants.ITWSEESSONGINX_INGRESS_HTTPS_NODEPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITWSEESSONGINX_INGRESS_HTTP_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.ITWSEESSONGINX_INGRESS_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_WSEESSONGINX_INGRESS_HTTPS_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_WSEESSONGINX_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_WSEESSONGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; @@ -172,8 +172,8 @@ public void initAll(@Namespaces(4) List namespaces) { installAndVerifyOperator(opNamespace, domain1Namespace, domain2Namespace); if (!OKD) { // install and verify NGINX - nginxHelmParams = installAndVerifyNginx(nginxNamespace, ITWSEESSONGINX_INGRESS_HTTP_NODEPORT, - ITWSEESSONGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, (OKE_CLUSTER ? null : "NodePort")); + nginxHelmParams = installAndVerifyNginx(nginxNamespace, IT_WSEESSONGINX_INGRESS_HTTP_NODEPORT, + IT_WSEESSONGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, (OKE_CLUSTER ? null : "NodePort")); String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; logger.info("NGINX service name: {0}", nginxServiceName); @@ -257,7 +257,7 @@ private String checkWSDLAccess(String domainNamespace, String domainUid, if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { // to access app url in podman we have to use mapped nodeport and localhost String url = "http://" + formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) - + ":" + ITWSEESSONGINX_INGRESS_HTTP_HOSTPORT + appURI; + + ":" + IT_WSEESSONGINX_INGRESS_HTTP_HOSTPORT + appURI; assertEquals(200, OracleHttpClient.get(url, true).statusCode()); // to access app url inside admin pod we have to use nodehost and nodeport return "http://" + K8S_NODEPORT_HOST + ":" + serviceTestNodePort + appURI; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 018fb4a5e9d..bcc99c38abd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -223,16 +223,17 @@ public interface TestConstants { public static final String TRAEFIK_RELEASE_NAME = "traefik-release" + BUILD_ID; public static final String TRAEFIK_REPO_NAME = "traefik"; public static final String TRAEFIK_CHART_NAME = "traefik"; + public static final String TRAEFIK_CHART_VERSION = "25.0.0"; public static final String TRAEFIK_INGRESS_IMAGE_NAME = TEST_IMAGES_TENANCY + "/test-images/traefik"; public static final String TRAEFIK_INGRESS_IMAGE_REGISTRY = TEST_IMAGES_REPO; - public static final String TRAEFIK_INGRESS_IMAGE_TAG = "v2.10.5"; + public static final String TRAEFIK_INGRESS_IMAGE_TAG = "v3.0.0"; public static final String TRAEFIK_NAMESPACE = "ns-traefik"; - public static final int TRAEFIK_INGRESS_HTTP_NODEPORT = 30880; - public static final int TRAEFIK_INGRESS_HTTPS_NODEPORT = 30443; + public static final int TRAEFIK_INGRESS_HTTP_NODEPORT = 30080; public static final int TRAEFIK_INGRESS_HTTP_HOSTPORT = 2080; - public static final int TRAEFIK_INGRESS_HTTPS_HOSTPORT = 2443; - + public static final int TRAEFIK_INGRESS_HTTPS_NODEPORT = 30443; + public static final int TRAEFIK_INGRESS_HTTPS_HOSTPORT = 2043; + // ELK Stack and WebLogic logging exporter constants public static final String ELASTICSEARCH_NAME = "elasticsearch"; public static final String ELASTICSEARCH_IMAGE_NAME = ARM ? TEST_IMAGES_PREFIX + "test-images/elasticsearch" @@ -476,43 +477,88 @@ public interface TestConstants { "largedomaintesting.props"; //node ports used by the integration tests - public static final int ITEXTERNALNODEPORTSERVICE_CONAINERPORT = 32156; - public static final int ITEXTERNALNODEPORTSERVICE_HOSTPORT = 2156; + public static final int IT_EXTERNALNODEPORTSERVICE_NODEPORT = 31000; + public static final int IT_EXTERNALNODEPORTSERVICE_HOSTPORT = 2100; - public static final int IT_DEDICATED_MODE_CONAINERPORT = 32159; - public static final int IT_DEDICATED_MODE_HOSTPORT = 2160; + public static final int IT_DEDICATEDMODE_NODEPORT = 31004; + public static final int IT_DEDICATEDMODE_HOSTPORT = 2104; - public static final int IT_EXTERNALLB_TUNNELING_HTTP_CONAINERPORT = 32169; - public static final int IT_EXTERNALLB_TUNNELING_HTTP_HOSTPORT = 2172; - public static final int IT_EXTERNALLB_TUNNELING_HTTPS_CONAINERPORT = 32170; - public static final int IT_EXTERNALLB_TUNNELING_HTTPS_HOSTPORT = 2173; + public static final int IT_EXTERNALLBTUNNELING_HTTP_NODEPORT = 31008; + public static final int IT_EXTERNALLBTUNNELING_HTTP_HOSTPORT = 2108; + public static final int IT_EXTERNALLBTUNNELING_HTTPS_NODEPORT = 31012; + public static final int IT_EXTERNALLBTUNNELING_HTTPS_HOSTPORT = 2112; - public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_CONAINERPORT = 32189; - public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_HOSTPORT = 2182; - public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_CONAINERPORT = 32185; - public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_HOSTPORT = 2175; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_NODEPORT = 31016; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTP_HOSTPORT = 2116; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_NODEPORT = 31020; + public static final int IT_ITMIIDOMAINUPGRADETOSECUREMODE_HTTPS_HOSTPORT = 2120; - public static final int IT_MONITORINGEXPORTER_PROM_HTTP_CONAINERPORT = 32143; - public static final int IT_MONITORINGEXPORTER_PROM_HTTP_HOSTPORT = 2143; - public static final int IT_MONITORINGEXPORTER_ALERT_HTTP_CONAINERPORT = 32343; - public static final int IT_MONITORINGEXPORTER_ALERT_HTTP_HOSTPORT = 2343; + public static final int IT_MONITORINGEXPORTER_PROMETHEUS_HTTP_NODEPORT = 31024; + public static final int IT_MONITORINGEXPORTER_PROMETHEUS_HTTP_HOSTPORT = 2124; + public static final int IT_MONITORINGEXPORTER_ALERT_HTTP_NODEPORT = 31028; + public static final int IT_MONITORINGEXPORTER_ALERT_HTTP_HOSTPORT = 2128; + + public static final int IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTP_NODEPORT = 31032; + public static final int IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTP_HOSTPORT = 2132; + public static final int IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTPS_NODEPORT = 31036; + public static final int IT_MONITORINGEXPORTERWEBAPP_NGINX_HTTPS_HOSTPORT = 2136; - public static final int IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_CONAINERPORT = 32331; - public static final int IT_ISTIOMONITORINGEXPORTER_PROM_HTTP_HOSTPORT = 2331; - - public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT = 30881; - public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT = 30444; - public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT = 2081; - public static final int ITLBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT = 2444; + public static final int IT_MONITORINGEXPORTERWEBAPP_PROMETHEUS_HTTP_NODEPORT = 31040; + public static final int IT_MONITORINGEXPORTERWEBAPP_PROMETHEUS_HTTP_HOSTPORT = 2140; + public static final int IT_MONITORINGEXPORTERWEBAPP_ALERT_HTTP_NODEPORT = 31044; + public static final int IT_MONITORINGEXPORTERWEBAPP_ALERT_HTTP_HOSTPORT = 2144; + + public static final int IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTP_NODEPORT = 31048; + public static final int IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTP_HOSTPORT = 2148; + public static final int IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTPS_NODEPORT = 31052; + public static final int IT_MONITORINGEXPORTERSAMPLES_NGINX_HTTPS_HOSTPORT = 2152; + + public static final int IT_MONITORINGEXPORTERSIDECAR_PROMETHEUS_HTTP_NODEPORT = 31056; + public static final int IT_MONITORINGEXPORTERSIDECAR_PROMETHEUS_HTTP_HOSTPORT = 2156; + public static final int IT_MONITORINGEXPORTERSIDECAR_ALERT_HTTP_NODEPORT = 31060; + public static final int IT_MONITORINGEXPORTERSIDECAR_ALERT_HTTP_HOSTPORT = 2160; - public static final int ITWSEESSONGINX_INGRESS_HTTP_NODEPORT = 31781; - public static final int ITWSEESSONGINX_INGRESS_HTTPS_NODEPORT = 31744; - public static final int ITWSEESSONGINX_INGRESS_HTTP_HOSTPORT = 2781; - public static final int ITWSEESSONGINX_INGRESS_HTTPS_HOSTPORT = 2782; + public static final int IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_NODEPORT = 31064; + public static final int IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_HOSTPORT = 2164; + public static final int IT_MONITORINGEXPORTERMF_ALERT_HTTP_NODEPORT = 31068; + public static final int IT_MONITORINGEXPORTERMF_ALERT_HTTP_HOSTPORT = 2168; - public static final int ITHPACUSTOMNGINX_INGRESS_HTTP_NODEPORT = 31785; - public static final int ITHPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT = 31746; - public static final int ITHPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT = 2785; - public static final int ITHPACUSTOMNGINX_INGRESS_HTTPS_HOSTPORT = 2786; + public static final int ITHORIZONTALPODSCALER_PROMETHEUS_HTTP_NODEPORT = 31072; + public static final int ITHORIZONTALPODSCALER_PROMETHEUS_HTTP_HOSTPORT = 2172; + public static final int ITHORIZONTALPODSCALER_ALERT_HTTP_CONAINERPORT = 31076; + public static final int ITHORIZONTALPODSCALER_ALERT_HTTP_HOSTPORT = 2176; + + public static final int IT_ISTIOMONITORINGEXPORTER_PROMETHEUS_HTTP_NODEPORT = 31080; + public static final int IT_ISTIOMONITORINGEXPORTER_PROMETHEUS_HTTP_HOSTPORT = 2180; + public static final int IT_LBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT = 31084; + public static final int IT_LBTWODOMAINSNGINX_INGRESS_HTTP_HOSTPORT = 2184; + public static final int IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT = 31088; + public static final int IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_HOSTPORT = 2188; + + public static final int IT_WSEESSONGINX_INGRESS_HTTP_NODEPORT = 31092; + public static final int IT_WSEESSONGINX_INGRESS_HTTP_HOSTPORT = 2192; + public static final int IT_WSEESSONGINX_INGRESS_HTTPS_NODEPORT = 31096; + public static final int IT_WSEESSONGINX_INGRESS_HTTPS_HOSTPORT = 2196; + + public static final int IT_HPACUSTOMNGINX_INGRESS_HTTP_NODEPORT = 31100; + public static final int IT_HPACUSTOMNGINX_INGRESS_HTTP_HOSTPORT = 2200; + public static final int IT_HPACUSTOMNGINX_INGRESS_HTTPS_NODEPORT = 31104; + public static final int IT_HPACUSTOMNGINX_INGRESS_HTTPS_HOSTPORT = 2204; + + public static final int IT_WEBAPPACCESSNGINX_INGRESS_HTTP_NODEPORT = 31108; + public static final int IT_WEBAPPACCESSNGINX_INGRESS_HTTP_HOSTPORT = 2208; + public static final int IT_WEBAPPACCESSNGINX_INGRESS_HTTPS_NODEPORT = 31112; + public static final int IT_WEBAPPACCESSNGINX_INGRESS_HTTPS_HOSTPORT = 2212; + + public static final int IT_MONITORINGEXPORTERSAMPLES_PROMETHEUS_HTTP_NODEPORT = 31116; + public static final int IT_MONITORINGEXPORTERSAMPLES_PROMETHEUS_HTTP_HOSTPORT = 2216; + public static final int IT_MONITORINGEXPORTERSAMPLES_ALERT_HTTP_NODEPORT = 31120; + public static final int IT_MONITORINGEXPORTERSAMPLES_ALERT_HTTP_HOSTPORT = 2220; + + public static final int IT_REMOTECONSOLENGINX_INGRESS_HTTP_NODEPORT = 31124; + public static final int IT_REMOTECONSOLENGINX_INGRESS_HTTP_HOSTPORT = 2224; + public static final int IT_REMOTECONSOLENGINX_INGRESS_HTTPS_NODEPORT = 31128; + public static final int IT_REMOTECONSOLENGINX_INGRESS_HTTPS_HOSTPORT = 2228; + } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java index e9a395b5ba1..9302ff91cf9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.actions; @@ -454,6 +454,29 @@ public static boolean scaleClusterWithRestApi(String domainUid, externalRestHttpsPort, opNamespace, opServiceAccount); } + /** + * Scale the cluster of the domain in the specified namespace using REST API. + * + * @param domainUid domainUid of the domain to be scaled + * @param clusterName name of the WebLogic cluster to be scaled in the domain + * @param numOfServers number of servers to be scaled to + * @param opPodName operator pod name + * @param opPort operator port + * @param opNamespace namespace of WebLogic operator + * @param opServiceAccount the service account for operator + * @return true if REST call succeeds, false otherwise + */ + public static boolean scaleClusterWithRestApiInOpPod(String domainUid, + String clusterName, + int numOfServers, + String opPodName, + int opPort, + String opNamespace, + String opServiceAccount) { + return Domain.scaleClusterWithRestApiInOpPod(domainUid, clusterName, numOfServers, + opPodName, opPort, opNamespace, opServiceAccount); + } + /** * Scale the cluster of the domain in the specified namespace with REST API. * diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java index 05a40edcebe..7792a7dbfd4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java @@ -27,12 +27,14 @@ import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; import oracle.weblogic.kubernetes.utils.FileUtils; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.PROJECT_ROOT; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; @@ -478,7 +480,89 @@ public static boolean scaleClusterWithRestApi(String domainUid, "Calling curl command"); return true; } - + + /** + * Scale the cluster of the domain in the specified namespace with REST API. + * + * @param domainUid domainUid of the domain to be scaled + * @param clusterName name of the WebLogic cluster to be scaled in the domain + * @param numOfServers number of servers to be scaled to + * @param opPodName operator pod name + * @param opPort operator port + * @param opNamespace namespace of WebLogic operator + * @param opServiceAccount the service account for operator + * @return true if REST call succeeds, false otherwise + */ + public static boolean scaleClusterWithRestApiInOpPod(String domainUid, + String clusterName, + int numOfServers, + String opPodName, + int opPort, + String opNamespace, + String opServiceAccount) { + LoggingFacade logger = getLogger(); + + logger.info("Getting the secret of service account {0} in namespace {1}", opServiceAccount, opNamespace); + String secretName = Secret.getSecretOfServiceAccount(opNamespace, opServiceAccount); + if (secretName.isEmpty()) { + logger.info("Did not find secret of service account {0} in namespace {1}", opServiceAccount, opNamespace); + return false; + } + logger.info("Got secret {0} of service account {1} in namespace {2}", secretName, opServiceAccount, opNamespace); + + logger.info("Getting service account token stored in secret {0} to authenticate as service account {1}" + + " in namespace {2}", secretName, opServiceAccount, opNamespace); + String secretToken = Secret.getSecretEncodedToken(opNamespace, secretName); + if (secretToken.isEmpty()) { + logger.info("Did not get encoded token for secret {0} associated with service account {1} in namespace {2}", + secretName, opServiceAccount, opNamespace); + return false; + } + logger.info("Got encoded token for secret {0} associated with service account {1} in namespace {2}: {3}", + secretName, opServiceAccount, opNamespace, secretToken); + + // decode the secret encoded token + String decodedToken = OKD ? secretToken : new String(Base64.getDecoder().decode(secretToken)); + logger.info("Got decoded token for secret {0} associated with service account {1} in namespace {2}: {3}", + secretName, opServiceAccount, opNamespace, decodedToken); + assertNotNull(decodedToken, "Couldn't get secret, token is null"); + + // build the curl command to scale the cluster + String command = new StringBuffer() + .append("curl -g --noproxy '*' -v -k ") + .append("-H \"Authorization:Bearer ") + .append(decodedToken) + .append("\" ") + .append("-H Accept:application/json ") + .append("-H Content-Type:application/json ") + .append("-H X-Requested-By:MyClient ") + .append("-d '{\"spec\": {\"replicas\": ") + .append(numOfServers) + .append("} }' ") + .append("-X POST https://") + .append(opPodName) + .append(":") + .append(opPort) + .append("/operator/latest/domains/") + .append(domainUid) + .append("/clusters/") + .append(clusterName) + .append("/scale").toString(); + + String commandToRun = KUBERNETES_CLI + " exec -n " + opNamespace + " " + opPodName + " -- " + command; + logger.info("curl command to run in pod {0} is: {1}", opPodName, commandToRun); + + ExecResult result = null; + try { + result = ExecCommand.exec(commandToRun, true); + logger.info("result is: {0}", result.toString()); + } catch (IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + + return true; + } + /** * Scale the cluster of the domain in the specified namespace with REST API. * diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index 4b5b1d56e04..ddbd0a4ae1a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -1256,7 +1256,7 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost, if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { String channel = "internal-t3"; - int port = getServicePort(domainNamespace, getExternalServicePodName(adminServerPodName), + int port = getServicePort(domainNamespace, adminServerPodName, sslChannelName.isEmpty() ? channel : sslChannelName); String domainName = adminServerPodName.split("-" + ADMIN_SERVER_NAME_BASE)[0]; String serviceName = ADMIN_SERVER_NAME_BASE; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index 976ebbf4b7d..6967989aff1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -100,6 +100,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; import static oracle.weblogic.kubernetes.actions.TestActions.scaleCluster; import static oracle.weblogic.kubernetes.actions.TestActions.scaleClusterWithRestApi; +import static oracle.weblogic.kubernetes.actions.TestActions.scaleClusterWithRestApiInOpPod; import static oracle.weblogic.kubernetes.actions.TestActions.scaleClusterWithWLDF; import static oracle.weblogic.kubernetes.actions.impl.UniqueName.random; import static oracle.weblogic.kubernetes.assertions.TestAssertions.credentialsNotValid; @@ -465,7 +466,8 @@ public static void scaleAndVerifyCluster(String clusterName, String myWebAppName, String curlCmdForWLDFApp, String curlCmd, - List expectedServerNames) { + List expectedServerNames, + String... args) { LoggingFacade logger = getLogger(); // get the original managed server pod creation timestamp before scale List listOfPodCreationTimestamp = new ArrayList<>(); @@ -482,13 +484,25 @@ public static void scaleAndVerifyCluster(String clusterName, logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers", clusterName, domainUid, domainNamespace, replicasAfterScale); if (withRestApi) { - assertThat(assertDoesNotThrow(() -> scaleClusterWithRestApi(domainUid, clusterName, - replicasAfterScale, externalRestHttpsPort, opNamespace, opServiceAccount))) - .as(String.format("Verify scaling cluster %s of domain %s in namespace %s with REST API succeeds", - clusterName, domainUid, domainNamespace)) - .withFailMessage(String.format("Scaling cluster %s of domain %s in namespace %s with REST API failed", - clusterName, domainUid, domainNamespace)) - .isTrue(); + if (OKE_CLUSTER && args != null && args.length > 0) { + String operatorPodName = (args == null || args.length == 0) ? null : args[0]; + int opExtPort = 8081; + assertThat(assertDoesNotThrow(() -> scaleClusterWithRestApiInOpPod(domainUid, clusterName, + replicasAfterScale, operatorPodName, opExtPort, opNamespace, opServiceAccount))) + .as(String.format("Verify scaling cluster %s of domain %s in namespace %s with REST API succeeds", + clusterName, domainUid, domainNamespace)) + .withFailMessage(String.format("Scaling cluster %s of domain %s in namespace %s with REST API failed", + clusterName, domainUid, domainNamespace)) + .isTrue(); + } else { + assertThat(assertDoesNotThrow(() -> scaleClusterWithRestApi(domainUid, clusterName, + replicasAfterScale, externalRestHttpsPort, opNamespace, opServiceAccount))) + .as(String.format("Verify scaling cluster %s of domain %s in namespace %s with REST API succeeds", + clusterName, domainUid, domainNamespace)) + .withFailMessage(String.format("Scaling cluster %s of domain %s in namespace %s with REST API failed", + clusterName, domainUid, domainNamespace)) + .isTrue(); + } } else if (withWLDF) { // scale the cluster using WLDF policy assertThat(assertDoesNotThrow(() -> scaleClusterWithWLDF(clusterName, domainUid, domainNamespace, @@ -1337,7 +1351,7 @@ public static String getHostAndPort(String hostName, int servicePort) { * @return formatted for ipv6 */ public static String formatIPv6Host(String hostname) { - return hostname.contains(":") ? "[" + hostname + "]" : hostname; + return hostname.contains(":") ? hostname.contains("[") ? hostname : "[" + hostname + "]" : hostname; } /** @@ -2359,7 +2373,6 @@ public static String createIngressHostRouting(String domainNamespace, String dom * @param port container port of the service * @param annoations ingress annotations * @param tlsList list of tls secrets - * @param isSecureMode if TLS * @return hostheader host header */ public static String createIngressHostRouting(String domainNamespace, String domainUid, @@ -2386,7 +2399,7 @@ public static String createIngressHostRouting(String domainNamespace, String dom .paths(Collections.singletonList(httpIngressPath))); ingressRules.add(ingressRule); - String ingressName = domainNamespace + "-" + domainUid + "-" + serviceName + '-' + port; + String ingressName = domainNamespace + "-" + domainUid + "-" + serviceName + "-" + port; assertDoesNotThrow(() -> createIngress(ingressName, domainNamespace, annoations, Files.readString(INGRESS_CLASS_FILE_NAME), ingressRules, tlsList)); @@ -2447,6 +2460,49 @@ public static void createIngressPathRouting(String domainNamespace, String domai getLogger().info("ingress {0} was created in namespace {1}", ingressName, domainNamespace); } + /** + * Create ingress resource for a single service. + * + * @param namespace namespace in which the service exists + * @param path path prefix + * @param serviceName name of the service for which to create ingress routing + * @param port container port of the service + * @param ingressClassName ingress class name + * @param host ingress host name + */ + public static void createIngressPathRouting(String namespace, String path, + String serviceName, int port, String ingressClassName, + String host) { + // create an ingress in domain namespace + V1HTTPIngressPath httpIngressPath = new V1HTTPIngressPath() + .path(path) + .pathType("Prefix") + .backend(new V1IngressBackend() + .service(new V1IngressServiceBackend() + .name(serviceName) + .port(new V1ServiceBackendPort().number(port))) + ); + + // create ingress rule + List ingressRules = new ArrayList<>(); + V1IngressRule ingressRule = new V1IngressRule() + .host(host) + .http(new V1HTTPIngressRuleValue() + .paths(Collections.singletonList(httpIngressPath))); + ingressRules.add(ingressRule); + + String ingressName = namespace + "-" + serviceName; + assertDoesNotThrow(() -> createIngress(ingressName, namespace, null, + ingressClassName, ingressRules, null)); + + // check the ingress was found in the domain namespace + assertThat(assertDoesNotThrow(() -> listIngresses(namespace))) + .as(String.format("Test ingress %s was found in namespace %s", ingressName, namespace)) + .withFailMessage(String.format("Ingress %s was not found in namespace %s", ingressName, namespace)) + .contains(ingressName); + getLogger().info("ingress {0} was created in namespace {1}", ingressName, namespace); + } + /** * Create ingress resource for a single service. * @@ -2457,7 +2513,7 @@ public static void createIngressPathRouting(String domainNamespace, String domai * @param ingressClassName ingress class name */ public static void createIngressPathRouting(String namespace, String path, - String serviceName, int port, String ingressClassName) { + String serviceName, int port, String ingressClassName) { // create an ingress in domain namespace V1HTTPIngressPath httpIngressPath = new V1HTTPIngressPath() .path(path) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java index a1f3f628cc3..3a8da15def8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLProtocolException; import io.kubernetes.client.custom.IntOrString; @@ -104,6 +105,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; +import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; @@ -1094,6 +1096,61 @@ public static void deleteHostPathProvisioner(String namespace) { assertTrue(response, "Failed to delete hostpath provisioner"); } + /** + * Create file with sql command in the pod. + * @param namespace pod's namespace + * @param podName name of pod + * @param sqlCommand sql command + * @param fileName name of file + */ + public static void createSqlFileInPod(String podName, String namespace, + String sqlCommand, String fileName) throws IOException { + + Path sourceFile = Files.writeString(Paths.get(WORK_DIR, fileName), sqlCommand); + LoggingFacade logger = getLogger(); + StringBuffer mysqlCmd = new StringBuffer("cat " + sourceFile.toString() + " | "); + mysqlCmd.append(KUBERNETES_CLI + " exec -i -n "); + mysqlCmd.append(namespace); + mysqlCmd.append(" "); + mysqlCmd.append(podName); + mysqlCmd.append(" -- /bin/bash -c \""); + mysqlCmd.append("cat > /tmp/" + fileName + "\""); + logger.info("mysql command {0}", mysqlCmd.toString()); + ExecResult result = assertDoesNotThrow(() -> exec(new String(mysqlCmd), false)); + logger.info("mysql returned {0}", result.toString()); + logger.info("mysql returned EXIT value {0}", result.exitValue()); + assertEquals(0, result.exitValue(), "mysql execution fails"); + } + + /** + * Run mysql inside the pod. + * @param namespace pod's namespace + * @param podName name of pod + * @param sqlFilePath name of sql file + * @param password name of file + */ + public static void runMysqlInsidePod(String podName, String namespace, String password, String sqlFilePath) { + final LoggingFacade logger = getLogger(); + + logger.info("Sleeping for 1 minute before connecting to mysql db"); + assertDoesNotThrow(() -> TimeUnit.MINUTES.sleep(1)); + StringBuffer mysqlCmd = new StringBuffer(KUBERNETES_CLI + " exec -i -n "); + mysqlCmd.append(namespace); + mysqlCmd.append(" "); + mysqlCmd.append(podName); + mysqlCmd.append(" -- /bin/bash -c \""); + mysqlCmd.append("mysql --force "); + mysqlCmd.append("-u root -p" + password); + mysqlCmd.append(" < "); + mysqlCmd.append(sqlFilePath); + mysqlCmd.append(" \""); + logger.info("mysql command {0}", mysqlCmd.toString()); + ExecResult result = assertDoesNotThrow(() -> exec(new String(mysqlCmd), true)); + logger.info("mysql returned {0}", result.toString()); + logger.info("mysql returned EXIT value {0}", result.exitValue()); + assertEquals(0, result.exitValue(), "mysql execution fails"); + } + // create hostpath provisioner using api. private static void createHostPathProvisionerObjects(String namespace, String hostPath) throws ApiException { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index 8826afa7ead..223d478c938 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -4,6 +4,8 @@ package oracle.weblogic.kubernetes.utils; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -67,6 +69,7 @@ import static oracle.weblogic.kubernetes.TestConstants.GRAFANA_REPO_URL; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MONITORING_EXPORTER_BRANCH; @@ -88,6 +91,7 @@ import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_REPO_URL; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MONITORING_EXPORTER_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deleteSecret; @@ -234,6 +238,7 @@ public static void cloneMonitoringExporter(String monitoringExporterSrcDir) { .execute()); } + /** * Check metrics using Prometheus. * @@ -242,13 +247,45 @@ public static void cloneMonitoringExporter(String monitoringExporterSrcDir) { * @param hostPortPrometheus host:nodePort for prometheus * @throws Exception if command to check metrics fails */ - public static void checkMetricsViaPrometheus(String searchKey, String expectedVal, String hostPortPrometheus) + public static void checkMetricsViaPrometheus(String searchKey, String expectedVal, + String hostPortPrometheus) throws Exception { LoggingFacade logger = getLogger(); // url String curlCmd = - String.format("curl -g --silent --show-error --noproxy '*' http://%s/api/v1/query?query=%s", + String.format("curl -g --silent --show-error --noproxy '*' -H 'host: *'" + + " http://%s/api/v1/query?query=%s", + hostPortPrometheus, searchKey); + + logger.info("Executing Curl cmd {0}", curlCmd); + logger.info("Checking searchKey: {0}", searchKey); + logger.info(" expected Value {0} ", expectedVal); + testUntil( + searchForKey(curlCmd, expectedVal), + logger, + "Check prometheus metric {0} against expected {1}", + searchKey, + expectedVal); + } + + /** + * Check metrics using Prometheus. + * + * @param searchKey - metric query expression + * @param expectedVal - expected metrics to search + * @param hostPortPrometheus host:nodePort for prometheus + * @throws Exception if command to check metrics fails + */ + public static void checkMetricsViaPrometheus(String searchKey, String expectedVal, + String hostPortPrometheus, String ingressHost) + throws Exception { + + LoggingFacade logger = getLogger(); + // url + String curlCmd = + String.format("curl -g --silent --show-error --noproxy '*' -H 'host: " + ingressHost + "'" + + " http://%s/api/v1/query?query=%s", hostPortPrometheus, searchKey); logger.info("Executing Curl cmd {0}", curlCmd); @@ -360,6 +397,7 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff * default is regex: default;domain1 * @param promHelmValuesFileDir path to prometheus helm values file directory * @param webhookNS namespace for webhook namespace + * @param ports optional prometheus and alert manager ports * @return the prometheus Helm installation parameters */ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuffix, @@ -367,7 +405,8 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff String promVersion, String prometheusRegexValue, String promHelmValuesFileDir, - String webhookNS) { + String webhookNS, + int...ports) { LoggingFacade logger = getLogger(); String prometheusReleaseName = "prometheus" + promReleaseSuffix; logger.info("create a staging location for prometheus scripts"); @@ -436,6 +475,10 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff } int promServerNodePort = getNextFreePort(); int alertManagerNodePort = getNextFreePort(); + if (ports.length != 0 && KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + promServerNodePort = ports[0]; + alertManagerNodePort = ports[1]; + } assertTrue(imageRepoLogin(TestConstants.BASE_IMAGES_REPO, BASE_IMAGES_REPO_USERNAME, BASE_IMAGES_REPO_PASSWORD), WLSIMG_BUILDER + " login failed"); @@ -1153,7 +1196,8 @@ public static void createAndVerifyDomain(String miiImage, * @param replicaCount number of managed servers * @param nodeport nginx nodeport */ - public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int replicaCount, int nodeport) { + public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int replicaCount, + int nodeport) throws UnknownHostException { List managedServerNames = new ArrayList<>(); for (int i = 1; i <= replicaCount; i++) { @@ -1162,6 +1206,9 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli // check that NGINX can access the sample apps from all managed servers in the domain String host = formatIPv6Host(K8S_NODEPORT_HOST); + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + host = InetAddress.getLocalHost().getHostAddress(); + } String curlCmd = String.format("curl -g --silent --show-error --noproxy '*' -H 'host: %s' http://%s:%s@%s:%s/wls-exporter/metrics", nginxHost, diff --git a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-onprem.yaml b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-onprem.yaml index 3534b95709a..ec846900872 100644 --- a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-onprem.yaml +++ b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-onprem.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: @domainuid@-http-ingress-rule diff --git a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-remoteconsole.yaml b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-remoteconsole.yaml index 5f38f3d4b73..ddc07e8732d 100644 --- a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-remoteconsole.yaml +++ b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-remoteconsole.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: diff --git a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-stickysession.yaml b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-stickysession.yaml index 46c3938dcee..b333d6e4ec4 100644 --- a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-stickysession.yaml +++ b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-stickysession.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2020, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -20,4 +20,4 @@ spec: port: 8001 sticky: cookie: - httpOnly: true \ No newline at end of file + httpOnly: true diff --git a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml index 33a428650e6..4bd0d0df0f7 100644 --- a/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml +++ b/integration-tests/src/test/resources/traefik/traefik-ingress-rules-tcp.yaml @@ -1,10 +1,10 @@ # Copyright (c) 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRouteTCP metadata: - name: production-secure-tls-passthrough + name: @NS@-mii-default-admin-admin-server-9002 namespace: @NS@ spec: entryPoints: diff --git a/integration-tests/src/test/resources/traefik/traefik-ingress-rules.yaml b/integration-tests/src/test/resources/traefik/traefik-ingress-rules.yaml index e176fabf637..dbe98f228d0 100644 --- a/integration-tests/src/test/resources/traefik/traefik-ingress-rules.yaml +++ b/integration-tests/src/test/resources/traefik/traefik-ingress-rules.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -19,7 +19,7 @@ spec: name: @domain1uid@-cluster-cluster-1 port: 7100 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -39,7 +39,7 @@ spec: tls: secretName: @domain1uid@-traefik-tls-secret --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -57,7 +57,7 @@ spec: name: @domain2uid@-cluster-cluster-1 port: 7100 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -77,7 +77,7 @@ spec: tls: secretName: @domain2uid@-tls-secret --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -95,7 +95,7 @@ spec: name: @domain1uid@-admin-server port: 7001 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -113,7 +113,7 @@ spec: name: @domain2uid@-admin-server port: 7001 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -132,7 +132,7 @@ spec: name: @domain1uid@-cluster-cluster-1 port: 7100 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: middleware-domain1 @@ -142,7 +142,7 @@ spec: regex: ^/domain1(.*) replacement: /$1 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -161,7 +161,7 @@ spec: name: @domain2uid@-cluster-cluster-1 port: 7100 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: middleware-domain2 diff --git a/integration-tests/src/test/resources/tunneling/traefik.tls.tunneling.template.yaml b/integration-tests/src/test/resources/tunneling/traefik.tls.tunneling.template.yaml index 22442310a41..503f57ca085 100644 --- a/integration-tests/src/test/resources/tunneling/traefik.tls.tunneling.template.yaml +++ b/integration-tests/src/test/resources/tunneling/traefik.tls.tunneling.template.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -28,7 +28,7 @@ spec: tls: {} --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: tls-middleware @@ -44,7 +44,7 @@ spec: sslRedirect: true --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: TLSStore metadata: name: default diff --git a/integration-tests/src/test/resources/tunneling/traefik.tunneling.template.yaml b/integration-tests/src/test/resources/tunneling/traefik.tunneling.template.yaml index 34e0fac35c2..83f9f340b56 100644 --- a/integration-tests/src/test/resources/tunneling/traefik.tunneling.template.yaml +++ b/integration-tests/src/test/resources/tunneling/traefik.tunneling.template.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: diff --git a/kubernetes/samples/charts/traefik/samples/host-routing.yaml b/kubernetes/samples/charts/traefik/samples/host-routing.yaml index 625419d235d..193b5869986 100644 --- a/kubernetes/samples/charts/traefik/samples/host-routing.yaml +++ b/kubernetes/samples/charts/traefik/samples/host-routing.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -17,7 +17,7 @@ spec: name: domain1-cluster-cluster-1 port: 8001 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: diff --git a/kubernetes/samples/charts/traefik/samples/path-routing.yaml b/kubernetes/samples/charts/traefik/samples/path-routing.yaml index f40776d9305..11055d4bede 100644 --- a/kubernetes/samples/charts/traefik/samples/path-routing.yaml +++ b/kubernetes/samples/charts/traefik/samples/path-routing.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -20,7 +20,7 @@ spec: name: domain1-cluster-cluster-1 port: 8001 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: middleware-domain1 @@ -30,7 +30,7 @@ spec: regex: ^/domain1(.*) replacement: /testwebapp$1 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -49,7 +49,7 @@ spec: name: domain2-cluster-cluster-1 port: 8001 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: middleware-domain2 diff --git a/kubernetes/samples/charts/traefik/samples/tls.yaml b/kubernetes/samples/charts/traefik/samples/tls.yaml index b93504d663b..60dea304328 100644 --- a/kubernetes/samples/charts/traefik/samples/tls.yaml +++ b/kubernetes/samples/charts/traefik/samples/tls.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2018, 2021, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: @@ -21,7 +21,7 @@ spec: tls: secretName: domain1-tls-cert --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: diff --git a/kubernetes/samples/quick-start/ingress-route.yaml b/kubernetes/samples/quick-start/ingress-route.yaml index 72a7c7a3474..11bf0a68140 100644 --- a/kubernetes/samples/quick-start/ingress-route.yaml +++ b/kubernetes/samples/quick-start/ingress-route.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: console @@ -15,7 +15,7 @@ spec: name: sample-domain1-admin-server port: 7001 --- -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: quickstart diff --git a/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-admin-server.yaml b/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-admin-server.yaml index b494504bfa4..072f5ced3f3 100644 --- a/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-admin-server.yaml +++ b/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-admin-server.yaml @@ -1,7 +1,7 @@ # Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-sample-domain1-admin-server diff --git a/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-cluster-cluster-1.yaml b/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-cluster-cluster-1.yaml index c9aa811bbff..682f034fa2a 100644 --- a/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-cluster-cluster-1.yaml +++ b/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain1-cluster-cluster-1.yaml @@ -24,7 +24,7 @@ # "curl -s -S -m 10 http://sample-domain1-cluster-cluster-1:8001/myapp_war/index.jsp" # -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-sample-domain1-cluster-cluster-1 diff --git a/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain2-cluster-cluster-1.yaml b/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain2-cluster-cluster-1.yaml index 9389c6793cf..8d7187fd284 100644 --- a/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain2-cluster-cluster-1.yaml +++ b/kubernetes/samples/scripts/create-weblogic-domain/ingresses/traefik-ingress-sample-domain2-cluster-cluster-1.yaml @@ -24,7 +24,7 @@ # "curl -s -S -m 10 http://sample-domain2-cluster-cluster-1:8001/myapp_war/index.jsp" # -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-sample-domain2-cluster-cluster-1 diff --git a/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/stage-and-create-ingresses.sh b/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/stage-and-create-ingresses.sh index ecbbe13b1b9..46e53faa5dc 100755 --- a/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/stage-and-create-ingresses.sh +++ b/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/stage-and-create-ingresses.sh @@ -112,7 +112,7 @@ do $(get_help "# " "$service_name") -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-$(get_service_name $service_name) @@ -149,7 +149,7 @@ EOF # Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-$(get_service_name $service_name) diff --git a/operator/integration-tests/model-in-image/mii-sample-wrapper/stage-and-create-ingresses.sh b/operator/integration-tests/model-in-image/mii-sample-wrapper/stage-and-create-ingresses.sh index a9efe0fcb73..354aa2f2670 100755 --- a/operator/integration-tests/model-in-image/mii-sample-wrapper/stage-and-create-ingresses.sh +++ b/operator/integration-tests/model-in-image/mii-sample-wrapper/stage-and-create-ingresses.sh @@ -112,7 +112,7 @@ do $(get_help "# " "$service_name") -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-$(get_service_name $service_name) @@ -149,7 +149,7 @@ EOF # Copyright (c) 2020, 2022, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik-ingress-$(get_service_name $service_name) From a119a73bca4b39630d29690cf8cbb78c8f06081d Mon Sep 17 00:00:00 2001 From: Sankar Neelakandan Date: Tue, 7 May 2024 12:43:29 -0700 Subject: [PATCH 054/356] add upgrade job to release/4.2 --- Jenkinsfile.podman | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 59237672340..f9989668037 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -21,7 +21,8 @@ def kind_k8s_map = [ ] def _kind_image = null CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential''' + H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential + H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.0''' pipeline { agent { label 'large-ol9' } From a39511c1a7983db8074384d429d3fd40bd9c8b06 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Wed, 8 May 2024 18:47:35 +0000 Subject: [PATCH 055/356] For Podman run move Fmw* domain tests to sequential suite to fix DB creation issue --- .../oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java | 2 +- .../test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index 26e5e981f8c..df1450ee021 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -69,7 +69,7 @@ * Tests to create JRF domain in persistent volume using WLST. */ @DisplayName("Verify the WebLogic server pods can run with domain created in persistent volume") -@Tag("kind-parallel") +@Tag("kind-sequential") @Tag("okd-fmw-cert") @IntegrationTest @Tag("olcne-sequential") diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index 53bf12844ca..cfec7cbbdc8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -93,7 +93,7 @@ */ @DisplayName("Test to create a FMW domain in persistent volume with new simplified feature") @IntegrationTest -@Tag("kind-parallel") +@Tag("kind-sequential") @Tag("oke-sequential1") @Tag("okd-fmw-cert") class ItFmwDomainOnPV { From ffdb1e3018d5dc423b5f2ee0de4927f0ae8cd05e Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Thu, 9 May 2024 09:33:47 +0000 Subject: [PATCH 056/356] Fix FMW sample nightly failure ItFmwDomainOnPVSample --- .../kubernetes/ItFmwDomainOnPVSample.java | 29 +++++++++---------- .../common/oracle.db.yaml | 2 -- .../domain-on-pv/run-test.sh | 2 -- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java index a4d05598503..efda901232c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java @@ -45,6 +45,7 @@ import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_REGISTRY; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_JAVA_HOME; @@ -135,12 +136,9 @@ public static void initAll(@Namespaces(4) List namespaces) { envMap.put("OKD", "" + OKD); envMap.put("KIND_CLUSTER", "" + KIND_CLUSTER); envMap.put("OCNE", "" + OCNE); - - if (OCNE) { - envMap.put("OPER_IMAGE_NAME", TEST_IMAGES_PREFIX + IMAGE_NAME_OPERATOR); - envMap.put("DOMAIN_CREATION_IMAGE_NAME", TEST_IMAGES_PREFIX + DOMAIN_CREATION_IMAGE_NAME); - envMap.put("DB_IMAGE_PULL_SECRET", BASE_IMAGES_REPO_SECRET_NAME); - } + envMap.put("OPER_IMAGE_NAME", TEST_IMAGES_PREFIX + IMAGE_NAME_OPERATOR); + envMap.put("DOMAIN_CREATION_IMAGE_NAME", TEST_IMAGES_PREFIX + DOMAIN_CREATION_IMAGE_NAME); + envMap.put("DB_IMAGE_PULL_SECRET", BASE_IMAGES_REPO_SECRET_NAME); // kind cluster uses openjdk which is not supported by image tool if (WIT_JAVA_HOME != null) { @@ -228,18 +226,19 @@ public void testInitialImage() { imagePull(BUSYBOX_IMAGE + ":" + BUSYBOX_TAG); imageTag(BUSYBOX_IMAGE + ":" + BUSYBOX_TAG, "busybox"); execTestScriptAndAssertSuccess("-initial-image", "Failed to run -initial-image"); + ExecResult result = Command.withParams( + new CommandParams() + .command(WLSIMG_BUILDER + " images") + .env(envMap) + .redirect(true) + ).executeAndReturnResult(); + logger.info(result.stdout()); // load the image to kind if using kind cluster String imageCreated; - if (KIND_REPO != null) { - imageCreated = DOMAIN_CREATION_IMAGE_NAME + ":" + DOMAIN_CREATION_IMAGE_JRF_TAG; - logger.info("loading image {0} to kind", imageCreated); - imagePush(imageCreated); - } else if (OCNE) { - imageCreated = TEST_IMAGES_PREFIX + DOMAIN_CREATION_IMAGE_NAME + ":" + DOMAIN_CREATION_IMAGE_JRF_TAG; - logger.info("pushing image {0} to repo", imageCreated); - imagePush(imageCreated); - } + imageCreated = TEST_IMAGES_PREFIX + DOMAIN_CREATION_IMAGE_NAME + ":" + DOMAIN_CREATION_IMAGE_JRF_TAG; + logger.info("pushing image {0} to repo", imageCreated); + imagePush(imageCreated); } /** diff --git a/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml b/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml index 6765d5b1f08..badba008c6e 100644 --- a/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml +++ b/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml @@ -64,10 +64,8 @@ spec: limits: cpu: "2" memory: "6Gi" - ephemeral-storage: "8Gi" requests: cpu: 500m - ephemeral-storage: "6Gi" terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst diff --git a/operator/integration-tests/domain-on-pv/run-test.sh b/operator/integration-tests/domain-on-pv/run-test.sh index 937e3a2dbbb..59c76879530 100755 --- a/operator/integration-tests/domain-on-pv/run-test.sh +++ b/operator/integration-tests/domain-on-pv/run-test.sh @@ -393,8 +393,6 @@ if [ "$DO_INITIAL_MAIN" = "true" ]; then testapp internal cluster-1 "Hello World!" if [ "$OKD" = "true" ]; then testapp OKD cluster-1 "Hello World!" - else - testapp traefik cluster-1 "Hello World!" fi fi From fda7c90b7bdd69abcb96e2d4d5fc001e54598b7d Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 9 May 2024 19:21:11 +0000 Subject: [PATCH 057/356] Merge branch 'rmarano/hide-14120-info' into 'main' Remove refs to 141200 See merge request weblogic-cloud/weblogic-kubernetes-operator!4697 (cherry picked from commit 88f525cf89574b90b42172c2e618612952443bac) bd51e48c Remove refs to 141200 --- .../upgrade-14210.md | 3 +- .../model-in-image/overview.md | 38 ------------------- .../model-in-image/runtime-updates.md | 2 - 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md index 8e2397d63cf..902f0716503 100644 --- a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md +++ b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md @@ -2,6 +2,7 @@ title = "Upgrade managed domains to v14.1.2.0" date = 2023-10-05T16:43:45-05:00 weight = 7 +draft = true pre = " " description = "Upgrade managed domains to v14.1.2.0." +++ @@ -443,7 +444,7 @@ serverPod: ### Sample WDT model for secured production mode and SSL -If you are upgrading an existing domain to 14.1.2.0 and your existing domain does not have secured production mode enabled, the operator, by default, will _disable_ secured production mode. If you want to override this behavior, you must enable it explicitly. Optionally, you can delete the existing domain and let the operator completely rebuild the domain and, by default, secured production mode will be enabled; you do not have to enable it explicitly. +If you are upgrading an existing domain to 14.1.2.0 and your existing domain does not have secured production mode enabled, the operator, by default, will _disable_ secured production mode. If you want to override this behavior, you must enable it explicitly. Optionally, you can delete the existing domain and let the operator completely rebuild the domain and, by default, secured production mode will be enabled; you do not have to enable it explicitly. The following is a code snippet of a WDT model for setting up secured production mode and SSL. diff --git a/documentation/site/content/managing-domains/model-in-image/overview.md b/documentation/site/content/managing-domains/model-in-image/overview.md index c8fdcb60d0d..6d6ce401b04 100644 --- a/documentation/site/content/managing-domains/model-in-image/overview.md +++ b/documentation/site/content/managing-domains/model-in-image/overview.md @@ -52,44 +52,6 @@ When you deploy a Model in Image domain resource YAML file: - The operator subsequently boots your domain's WebLogic Server pods. - The pods will obtain their domain home from the ConfigMap. -### Using demo SSL certificates in v14.1.2.0.0 or later - -{{% notice note %}} -Beginning with WebLogic Server version 14.1.2.0.0, when a domain is `production` mode enabled, it is automatically `secure mode` enabled, therefore, all communications with the domain are using SSL channels and non-secure listening ports are disabled. If there are no custom certificates configured for the SSL channels, then the server uses the demo SSL certificates. -The demo SSL certificates are now domain specific and generated when the domain is first created, -unlike previous releases, which were distributed with the WebLogic product installation. Oracle recommends using custom SSL -certificates in a production environment. -{{% /notice %}} - -The certificates are created under the domain home `security` folder. - -``` --rw-r----- 1 oracle oracle 1275 Feb 15 15:55 democakey.der --rw-r----- 1 oracle oracle 1070 Feb 15 15:55 democacert.der --rw-r----- 1 oracle oracle 1478 Feb 15 15:55 DemoTrust.p12 --rw-r----- 1 oracle oracle 1267 Feb 15 15:55 demokey.der --rw-r----- 1 oracle oracle 1099 Feb 15 15:55 democert.der --rw-r----- 1 oracle oracle 1144 Feb 15 15:55 DemoCerts.props --rw-r----- 1 oracle oracle 2948 Feb 15 15:55 DemoIdentity.p12 -``` - -For Model in Image domains, whenever you change any security credentials including, but not limited to, the Administration Server credentials, RCU credentials, and such, the domain will be recreated and a new set of demo SSL certificates will be generated. The SSL certificates are valid for 6 months, then they expire. - -The demo CA certificate expires in 5 years, however, whenever the domain is recreated, the entire set of certificates are regenerated so you _must_ import the demo CA certificate again. - -If you have any external client that needs to communicate with WebLogic Servers using SSL, then you need to import the current self-signing CA certificate, `democacert.der`, -into your local trust store. - -```shell - keytool -importcert -keystore -alias wlscacert -file $HOME/Downloads/democacer.der -``` - -If you are using the WebLogic Scripting Tool, before starting the WLST session, you can set the following system properties. - -```shell - export WLST_PROPERTIES="-Dweblogic.security.TrustKeyStore=DemoTrust -Dweblogic.security.SSL.ignoreHostnameVerification=true" -``` - ### Runtime updates Model updates can be applied at runtime by changing an image, secrets, a domain resource, or a WDT model ConfigMap after initial deployment. diff --git a/documentation/site/content/managing-domains/model-in-image/runtime-updates.md b/documentation/site/content/managing-domains/model-in-image/runtime-updates.md index 052f11b2378..38943cc0994 100644 --- a/documentation/site/content/managing-domains/model-in-image/runtime-updates.md +++ b/documentation/site/content/managing-domains/model-in-image/runtime-updates.md @@ -42,8 +42,6 @@ the change requires entirely shutting domain the domain, applying the change, and finally restarting the domain. Full domain restarts are described in [Full domain restarts]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#full-domain-restarts">}}). -**NOTE**: If you are using WebLogic Server 14.1.2.0.0 or later, see [Using demo SSL certificates in v14.1.2.0.0 or later]({{< relref "/managing-domains/model-in-image/overview#using-demo-ssl-certificates-in-v141200-or-later" >}}). - **NOTE**: Supported and unsupported changes are described in these sections: [Supported updates](#supported-updates) and [Unsupported updates](#unsupported-updates). _It is the administrator's responsibility to make the necessary changes to a domain resource to initiate the correct approach for an update._ From 36c63021526ba2a5738a14f76aeebf1cc40ef92c Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Mon, 13 May 2024 18:00:36 +0000 Subject: [PATCH 058/356] use the correct KUBE_VERSION for upgrade job --- Jenkinsfile.podman | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index f9989668037..5e2eeae2ebe 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -22,7 +22,7 @@ def kind_k8s_map = [ def _kind_image = null CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential - H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.0''' + H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24''' pipeline { agent { label 'large-ol9' } From d58f5f869a41efaff5a568e377a053bb9db2dea6 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Tue, 14 May 2024 13:33:42 +0000 Subject: [PATCH 059/356] Backport changes for Jenkinfile.oke to 4.2 --- Jenkinsfile.oke | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 04746f68781..ce27d402e33 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -2,8 +2,14 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // -CRON_SETTINGS = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false +CRON_SETTINGS_MAIN = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' + +CRON_SETTINGS_42 = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false + H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' + +CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-nightly' ? CRON_SETTINGS_MAIN : CRON_SETTINGS_42}" + pipeline { agent { label 'large' } options { @@ -12,8 +18,8 @@ pipeline { } triggers { // timer trigger for "nightly build" - parameterizedCron(env.JOB_NAME == 'wko-oke-nightly' ? - CRON_SETTINGS : '') + parameterizedCron(env.JOB_NAME == 'wko-oke-dev' ? + '' : CRON_SETTINGS) } tools { maven 'maven-3.8.7' @@ -138,7 +144,7 @@ pipeline { ) string(name: 'MOUNT_TARGET_OCID', description: 'only for debug runs on wko-oke-dev', - defaultValue: "test" + defaultValue: "ocid1.mounttarget.oc1.phx.aaaaaby27vhqpci5obuhqllqojxwiotqnb4c2ylefuzqaaaa" ) string(name: 'BRANCH', description: '', From 5e3c7615f58060eeecb691d992a00be44a748935 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Wed, 15 May 2024 20:41:22 +0000 Subject: [PATCH 060/356] Merge branch 'fix-unzip-returncode-check' into 'main' Fix unzip return code check to exclude warning mistaken as error See merge request weblogic-cloud/weblogic-kubernetes-operator!4691 (cherry picked from commit bd1b2c07dc388a7dfbc582b4e93d8a71601ef62c) 3b9bdebe Fix unzip return code check to exclude warning mistaken as error --- operator/src/main/resources/scripts/modelInImage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index f8536d7df91..96c7bbbb9b0 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -1349,7 +1349,7 @@ restoreAppAndLibs() { # zip, the original xml in the archive may have wdt tokenized notations. cd ${DOMAIN_HOME} || return 1 unzip -o ${IMG_ARCHIVES_ROOTDIR}/${file} -x "wlsdeploy/domainBin/*" "wlsdeploy/domainLibraries/*" "config/*" - if [ $? -ne 0 ] ; then + if [ $? -ne 0 ] && [ $ret -ne 11 ] ; then trace SEVERE "Domain Source Type is FromModel, error in extracting application archive ${IMG_ARCHIVES_ROOTDIR}/${file}" return 1 fi From 514172672ddc04553a0241ba2055ee383569ee62 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 16 May 2024 13:45:09 +0000 Subject: [PATCH 061/356] Merge branch 'fix-mii-script-error' into 'main' Fix unzip recode check error See merge request weblogic-cloud/weblogic-kubernetes-operator!4709 (cherry picked from commit 71a3eb62e150f5265dd9d3d0029296116f8c67fb) e849c848 Fix unzip recode check error --- operator/src/main/resources/scripts/modelInImage.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 96c7bbbb9b0..4a794450f33 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -1349,7 +1349,8 @@ restoreAppAndLibs() { # zip, the original xml in the archive may have wdt tokenized notations. cd ${DOMAIN_HOME} || return 1 unzip -o ${IMG_ARCHIVES_ROOTDIR}/${file} -x "wlsdeploy/domainBin/*" "wlsdeploy/domainLibraries/*" "config/*" - if [ $? -ne 0 ] && [ $ret -ne 11 ] ; then + ret=$? + if [ $ret -ne 0 ] && [ $ret -ne 11 ] ; then trace SEVERE "Domain Source Type is FromModel, error in extracting application archive ${IMG_ARCHIVES_ROOTDIR}/${file}" return 1 fi From b9e224bfce85970d6676703d2cf68ac69dbc1c49 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 16 May 2024 12:09:57 -0400 Subject: [PATCH 062/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index a9813f591b2..72626cde9fb 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -59,7 +59,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.12.0 + 3.13.0 oracle.kubernetes:jsonschema-maven-plugin diff --git a/pom.xml b/pom.xml index 5478826d2f3..fffd8b531b5 100644 --- a/pom.xml +++ b/pom.xml @@ -635,6 +635,11 @@ json-path-assert ${jsonpath-version}
    + + org.bitbucket.b_c + jose4j + ${jose4j-version} + @@ -689,6 +694,7 @@ 1.7.3 0.1.0 2.9.0 + 0.9.6 3.5.0 1.0.0 3.25.3 @@ -696,7 +702,7 @@ 4.2.1 19.0.1 3.0.1u2 - 1.9.23 + 1.9.24 4.12.0 3.9.0 1.78.1 @@ -710,8 +716,8 @@ 4.0.2 6.0.0 0.16.0 - 2.17.0 - 2.17.0 + 2.17.1 + 2.17.1 2.2 2.10.1 9.1.0 From a0aac045bc1d9384945bb0c7317c8c4e50ba03df Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 16 May 2024 14:45:03 -0400 Subject: [PATCH 063/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 72626cde9fb..872578d4d76 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -30,7 +30,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.12.0 + 3.13.0 org.junit.jupiter From e90d08a3cd3a94c08e77fce98e9e55ce05a02b6f Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 17 May 2024 01:18:58 +0000 Subject: [PATCH 064/356] Merge branch 'update-samples' into 'main' Update samples to use fully qualified CRD types with kubectl See merge request weblogic-cloud/weblogic-kubernetes-operator!4712 (cherry picked from commit e93b082fb4c77b5ee40d29460681371001a624e2) eafee943 Update samples to use fully qualified CRD types with kubectl --- .../samples/scripts/domain-lifecycle/helper.sh | 14 +++++++------- .../scripts/domain-lifecycle/introspectDomain.sh | 4 ++-- .../scripts/domain-lifecycle/restartServer.sh | 4 ++-- .../scripts/domain-lifecycle/rollCluster.sh | 6 +++--- .../samples/scripts/domain-lifecycle/rollDomain.sh | 4 ++-- .../scripts/domain-lifecycle/scaleCluster.sh | 6 +++--- .../scripts/domain-lifecycle/startCluster.sh | 6 +++--- .../scripts/domain-lifecycle/startDomain.sh | 4 ++-- .../scripts/domain-lifecycle/startServer.sh | 6 +++--- .../scripts/domain-lifecycle/stopCluster.sh | 6 +++--- .../samples/scripts/domain-lifecycle/stopDomain.sh | 4 ++-- .../samples/scripts/domain-lifecycle/stopServer.sh | 6 +++--- .../scripts/domain-lifecycle/waitForDomain.sh | 6 +++--- 13 files changed, 38 insertions(+), 38 deletions(-) diff --git a/kubernetes/samples/scripts/domain-lifecycle/helper.sh b/kubernetes/samples/scripts/domain-lifecycle/helper.sh index 8aa2f23893c..52ea11bc9cc 100644 --- a/kubernetes/samples/scripts/domain-lifecycle/helper.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/helper.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -978,9 +978,9 @@ getClusterResource() { clusterReferences=$(echo ${domainJson} | jq -r .spec.clusters[].name) for clusterReference in ${clusterReferences}; do - clusterNameFromReference=$(${kubernetesCli} get cluster "${clusterReference}" -n ${domainNamespace} -o json --ignore-not-found | jq -r .spec.clusterName) + clusterNameFromReference=$(${kubernetesCli} get cluster.v1.weblogic.oracle "${clusterReference}" -n ${domainNamespace} -o json --ignore-not-found | jq -r .spec.clusterName) if [ -z "${clusterNameFromReference}" ]; then - clusterNameFromReference=$(${kubernetesCli} get cluster "${clusterReference}" -n ${domainNamespace} -o json --ignore-not-found | jq -r .metadata.name) + clusterNameFromReference=$(${kubernetesCli} get cluster.vi.weblogic.oracle "${clusterReference}" -n ${domainNamespace} -o json --ignore-not-found | jq -r .metadata.name) fi if [ "${clusterNameFromReference}" == "${clusterName}" ]; then __clusterResource=$clusterReference @@ -1045,10 +1045,10 @@ executePatchCommand() { local verboseMode=$5 if [ "${verboseMode}" == "true" ]; then - printInfo "Executing command --> ${kubernetesCli} patch domain ${domainUid} \ + printInfo "Executing command --> ${kubernetesCli} patch domain.v9.weblogic.oracle ${domainUid} \ -n ${domainNamespace} --type=merge --patch \"${patchJson}\"" fi - ${kubernetesCli} patch domain ${domainUid} -n ${domainNamespace} --type=merge --patch "${patchJson}" + ${kubernetesCli} patch domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} --type=merge --patch "${patchJson}" } # @@ -1067,10 +1067,10 @@ executeClusterPatchCommand() { local verboseMode=$5 if [ "${verboseMode}" == "true" ]; then - printInfo "Executing command --> ${kubernetesCli} patch cluster ${clusterResource} \ + printInfo "Executing command --> ${kubernetesCli} patch cluster.v1.weblogic.oracle ${clusterResource} \ -n ${domainNamespace} --type=merge --patch \"${patchJson}\"" fi - ${kubernetesCli} patch cluster ${clusterResource} -n ${domainNamespace} --type=merge --patch "${patchJson}" + ${kubernetesCli} patch cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} --type=merge --patch "${patchJson}" } # timestamp diff --git a/kubernetes/samples/scripts/domain-lifecycle/introspectDomain.sh b/kubernetes/samples/scripts/domain-lifecycle/introspectDomain.sh index 0c0fb0bf18a..b812e194007 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/introspectDomain.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/introspectDomain.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -86,7 +86,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/restartServer.sh b/kubernetes/samples/scripts/domain-lifecycle/restartServer.sh index d7b7f86e764..10e5186a630 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/restartServer.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/restartServer.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -82,7 +82,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/rollCluster.sh b/kubernetes/samples/scripts/domain-lifecycle/rollCluster.sh index 880d4aa7939..724753b3b9e 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/rollCluster.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/rollCluster.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -98,7 +98,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 @@ -115,7 +115,7 @@ fi getClusterResource "${domainJson}" "${domainNamespace}" "${clusterName}" clusterResource # Get the cluster in json format -clusterJson=$(${kubernetesCli} get cluster ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) +clusterJson=$(${kubernetesCli} get cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${clusterJson}" ]; then printError "Unable to get cluster resource for cluster '${clusterName}' in namespace '${domainNamespace}'. Please make sure that a Cluster exists for cluster '${clusterName}' and that this Cluster is referenced by the Domain." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/rollDomain.sh b/kubernetes/samples/scripts/domain-lifecycle/rollDomain.sh index a94f0ee4cfd..b0f83475e54 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/rollDomain.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/rollDomain.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -86,7 +86,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/scaleCluster.sh b/kubernetes/samples/scripts/domain-lifecycle/scaleCluster.sh index 793862454ac..4c01cec1f5c 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/scaleCluster.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/scaleCluster.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -96,7 +96,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 @@ -113,7 +113,7 @@ fi getClusterResource "${domainJson}" "${domainNamespace}" "${clusterName}" clusterResource # Get the cluster in json format -clusterJson=$(${kubernetesCli} get cluster ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) +clusterJson=$(${kubernetesCli} get cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${clusterJson}" ]; then printError "Unable to get cluster resource for cluster '${clusterName}' in namespace '${domainNamespace}'. Please make sure that a Cluster exists for cluster '${clusterName}' and that this Cluster is referenced by the Domain." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/startCluster.sh b/kubernetes/samples/scripts/domain-lifecycle/startCluster.sh index de41524599c..6fb6144e869 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/startCluster.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/startCluster.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -90,7 +90,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 @@ -105,7 +105,7 @@ fi getClusterResource "${domainJson}" "${domainNamespace}" "${clusterName}" clusterResource -clusterJson=$(${kubernetesCli} get cluster ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) +clusterJson=$(${kubernetesCli} get cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${clusterJson}" ]; then printError "Unable to get cluster resource for cluster '${clusterName}' in namespace '${domainNamespace}'. Please make sure that a Cluster exists for cluster '${clusterName}' and that this Cluster is referenced by the Domain." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/startDomain.sh b/kubernetes/samples/scripts/domain-lifecycle/startDomain.sh index dabd48e3681..6c4eea2615e 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/startDomain.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/startDomain.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -74,7 +74,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Domain resource for domain '${domainUid}' not found in namespace '${domainNamespace}'. Exiting." diff --git a/kubernetes/samples/scripts/domain-lifecycle/startServer.sh b/kubernetes/samples/scripts/domain-lifecycle/startServer.sh index 888cb7c957a..608274bffd3 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/startServer.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/startServer.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -141,7 +141,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 @@ -157,7 +157,7 @@ fi if [ -n "${clusterName}" ]; then getClusterResource "${domainJson}" "${domainNamespace}" "${clusterName}" clusterResource - clusterJson=$(${kubernetesCli} get cluster ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) + clusterJson=$(${kubernetesCli} get cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${clusterJson}" ]; then printError "Unable to get cluster resource for cluster '${clusterName}' in namespace '${domainNamespace}'. Please make sure that a Cluster exists for cluster '${clusterName}' and that this Cluster is referenced by the Domain." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/stopCluster.sh b/kubernetes/samples/scripts/domain-lifecycle/stopCluster.sh index 5660839875d..8af8da54fbf 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/stopCluster.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/stopCluster.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -87,7 +87,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 @@ -102,7 +102,7 @@ fi getClusterResource "${domainJson}" "${domainNamespace}" "${clusterName}" clusterResource -clusterJson=$(${kubernetesCli} get cluster ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) +clusterJson=$(${kubernetesCli} get cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${clusterJson}" ]; then printError "Unable to get cluster resource for cluster '${clusterName}' in namespace '${domainNamespace}'. Please make sure that a Cluster exists for cluster '${clusterName}' and that this Cluster is referenced by the Domain." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/stopDomain.sh b/kubernetes/samples/scripts/domain-lifecycle/stopDomain.sh index ba7ec2e26b3..1bd76bdc8b0 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/stopDomain.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/stopDomain.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -72,7 +72,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Domain resource for domain '${domainUid}' not found in namespace '${domainNamespace}'. Exiting." diff --git a/kubernetes/samples/scripts/domain-lifecycle/stopServer.sh b/kubernetes/samples/scripts/domain-lifecycle/stopServer.sh index 1e060d2bec5..f784bb50d77 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/stopServer.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/stopServer.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -138,7 +138,7 @@ initialize() { initialize # Get the domain in json format -domainJson=$(${kubernetesCli} get domain ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) +domainJson=$(${kubernetesCli} get domain.v9.weblogic.oracle ${domainUid} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${domainJson}" ]; then printError "Unable to get domain resource for domain '${domainUid}' in namespace '${domainNamespace}'. Please make sure the 'domain_uid' and 'namespace' specified by the '-d' and '-n' arguments are correct. Exiting." exit 1 @@ -154,7 +154,7 @@ fi if [ -n "${clusterName}" ]; then getClusterResource "${domainJson}" "${domainNamespace}" "${clusterName}" clusterResource - clusterJson=$(${kubernetesCli} get cluster ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) + clusterJson=$(${kubernetesCli} get cluster.v1.weblogic.oracle ${clusterResource} -n ${domainNamespace} -o json --ignore-not-found) if [ -z "${clusterJson}" ]; then printError "Unable to get cluster resource for cluster '${clusterName}' in namespace '${domainNamespace}'. Please make sure that a Cluster exists for cluster '${clusterName}' and that this Cluster is referenced by the Domain." exit 1 diff --git a/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh b/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh index cca3456d38a..9df689caf81 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. set -eu @@ -246,7 +246,7 @@ getClusterValue() { local ljpath="{$3}" local attvalue set +e - attvalue=$(${KUBERNETES_CLI} -n ${DOMAIN_NAMESPACE} get cluster ${cname} -o=jsonpath="$ljpath" 2>&1) + attvalue=$(${KUBERNETES_CLI} -n ${DOMAIN_NAMESPACE} get cluster.v1.weblogic.oracle ${cname} -o=jsonpath="$ljpath" 2>&1) if [ $? -ne 0 ]; then if [ "$EXPECTED_STATE" = "Completed" ]; then trace "Error: Could not obtain '$ljpath' from cluster '${cname}' in namespace '${DOMAIN_NAMESPACE}'. Is your cluster resource deployed? Err='$attvalue'" @@ -256,7 +256,7 @@ getClusterValue() { attvalue='' fi fi - # echo "DEBUG ${KUBERNETES_CLI} -n ${DOMAIN_NAMESPACE} get cluster ${cname} -o=jsonpath=\"$ljpath\" 2>&1" + # echo "DEBUG ${KUBERNETES_CLI} -n ${DOMAIN_NAMESPACE} get cluster.v1.weblogic.oracle ${cname} -o=jsonpath=\"$ljpath\" 2>&1" # echo "DEBUG = '$attvalue'" eval "$__retvar='$attvalue'" set -e From f848e43b3974e2ea3f57e0e46bf8b7bc4f0c6c43 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 17 May 2024 13:48:06 +0000 Subject: [PATCH 065/356] Merge branch 'rmarano/change-k8s-setup' into 'main' Change K8s setup doc See merge request weblogic-cloud/weblogic-kubernetes-operator!4704 (cherry picked from commit ff14f6e146437aeb945d93bf2752caa9ba84305d) 29fdf66a change K8s setup doc bfdf1747 incorporate edits from Marina --- .../content/managing-operators/k8s-setup.md | 150 +++++------------- 1 file changed, 40 insertions(+), 110 deletions(-) diff --git a/documentation/site/content/managing-operators/k8s-setup.md b/documentation/site/content/managing-operators/k8s-setup.md index 30e50dd9198..676785b4af6 100644 --- a/documentation/site/content/managing-operators/k8s-setup.md +++ b/documentation/site/content/managing-operators/k8s-setup.md @@ -27,148 +27,78 @@ We have provided our hints and tips for several of these options in the followin ### Set up Kubernetes on bare compute resources in a cloud -Follow the basic steps from the [Terraform Kubernetes installer for Oracle Cloud Infrastructure](https://github.com/oracle/terraform-kubernetes-installer). +Follow the basic steps from the [Terraform OKE Module Installer for Oracle Cloud Infrastructure](https://oracle-terraform-modules.github.io/terraform-oci-oke/). #### Prerequisites -1. Download and install [Terraform](https://www.terraform.io/) (v0.10.3 or later). -2. Download and install the [Terraform Provider for Oracle Cloud Infrastructure](https://github.com/terraform-providers/terraform-provider-oci) (v2.0.0 or later). -3. Create an Terraform configuration file at `~/.terraformrc` that specifies the path to the Oracle Cloud Infrastructure provider: +1. Download and install the [Terraform OKE Module Installer for Oracle Cloud Infrastructure](https://github.com/oracle-terraform-modules/terraform-oci-oke). +1. Create directory for the Terraform module: ``` - providers { - oci = "/terraform-provider-oci" - } + $ mkdir terraformmodule + $ cd terraformmodule + ``` -4. Ensure that you have [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed if you plan to interact with the cluster locally. +1. Ensure that you have [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed if you plan to interact with the cluster locally. #### Quick start +The quick start uses the sample provided in [Multi-region service mesh with Istio and OKE](https://github.com/oracle-terraform-modules/terraform-oci-oke/tree/main/examples/istio-mc). + 1. Do a `git clone` of the Terraform Kubernetes Installer project: ```shell - $ git clone https://github.com/oracle/terraform-kubernetes-installer.git + $ git clone https://github.com/oracle-terraform-modules/terraform-oci-oke.git ``` -1. Initialize your project: +1. Run the following commands: ```shell - $ cd terraform-kubernetes-installer + $ cd terraform-oci-oke/examples + $ mkdir okewko + $ cp -rf istio-mc okewko + $ cd okewko ``` + +1. Edit `c1.tf` and `c2.tf` to add: + ```shell - $ terraform init + allow_bastion_cluster_access = true + bastion_is_public = true + control_plane_is_public = true ``` -1. Copy the example `terraform.tvfars`: - ```shell - $ cp terraform.example.tfvars terraform.tfvars + $ cp terraform.tfvars.example terraform.tfvars ``` -1. Edit the `terraform.tvfars` file to include values for your tenancy, user, and compartment. Optionally, edit the variables to change the `Shape` of the VMs for your Kubernetes master and workers, and your `etcd` cluster. For example: +1. In the `terraform.tfvars` file, update all values with the correct paths to the keys and IDs. - ```properties - #give a label to your cluster to help identify it if you have multiple - label_prefix="weblogic-operator-1-" +1. Run the commands: - #identification/authorization info - tenancy_ocid = "ocid1.tenancy...." - compartment_ocid = "ocid1.compartment...." - fingerprint = "..." - private_key_path = "/Users/username/.oci/oci_api_key.pem" - user_ocid = "ocid1.user..." + ```shell + $ terraform init + $ terraform plan + $ terraform apply --auto-approve + ``` - #shapes for your VMs - etcdShape = "VM.Standard1.2" - k8sMasterShape = "VM.Standard1.8" - k8sWorkerShape = "VM.Standard1.8" - k8sMasterAd1Count = "1" - k8sWorkerAd1Count = "2" + This will create two OKE clusters. - #this ingress is set to wide-open for testing **not secure** - etcd_ssh_ingress = "0.0.0.0/0" - master_ssh_ingress = "0.0.0.0/0" - worker_ssh_ingress = "0.0.0.0/0" - master_https_ingress = "0.0.0.0/0" - worker_nodeport_ingress = "0.0.0.0/0" +1. Log in to the OCI dashboard. - #create iscsi volumes to store your etcd and /var/lib/docker info - worker_iscsi_volume_create = true - worker_iscsi_volume_size = 100 - etcd_iscsi_volume_create = true - etcd_iscsi_volume_size = 50 - ``` + a. Go to Developer Services > OKE clusters. -1. Test and apply your changes: + b. Select c1 cluster > Access Cluster. + c. Copy and paste this command to create the kubeconfig, for example: ```shell - $ terraform plan - ``` - ```shell - $ terraform apply - ``` - -1. Test your cluster using the built-in script `scripts/cluster-check.sh`: + $ oci ce cluster create-kubeconfig --cluster-id ocid1.cluster.oc1...... --file $HOME/.kube/config --region us-phoenix-1 --token-version 2.0.0 --kube-endpoint PUBLIC_ENDPOINT - ```shell - $ scripts/cluster-check.sh - ``` -1. Output the SSH private key: - ``` - # output the ssh private key for use later - ``` - ```shell - $ rm -f generated/instances_id_rsa && terraform output ssh_private_key > generated/instances_id_rsa && chmod 600 generated/instances_id_rsa + $ export KUBECONFIG= $HOME/.kube/config ``` -1. If you need shared storage between your Kubernetes worker nodes, enable and configure NFS: - -In the current GA version, the Oracle Container Engine for Kubernetes supports network block storage that can be shared across nodes with access permission RWOnce (meaning that only one can write, others can read only). -If you choose to place your domain on a persistent volume, -you must use a shared file system to store the WebLogic domain configuration, which MUST be accessible from all the pods across the nodes. -Oracle recommends that you use the Oracle Cloud Infrastructure File Storage Service (or equivalent on other cloud providers). -Alternatively, you may install an NFS server on one node and share the file system across all the nodes. - -{{% notice note %}} Currently, we recommend that you use NFS version 3.0 for running WebLogic Server on Oracle Container Engine for Kubernetes. During certification, we found that when using NFS 4.0, the servers in the WebLogic domain went into a failed state intermittently. Because multiple threads use NFS (default store, diagnostics store, Node Manager, logging, and domain_home), there are issues when accessing the file store. These issues are removed by changing the NFS to version 3.0. -{{% /notice %}} - -```shell -$ terraform output worker_public_ips -``` -``` -IP1, -IP2 -``` -```shell -$ terraform output worker_private_ips -``` -``` -PRIVATE_IP1, -PRIVATE_IP2 -``` -```shell -$ ssh -i `pwd`/generated/instances_id_rsa opc@IP1 -``` -``` -worker-1$ sudo su - -worker-1# yum install -y nfs-utils -worker-1# mkdir /scratch -worker-1# echo "/scratch PRIVATE_IP2(rw)" >> /etc/exports -worker-1# systemctl restart nfs -worker-1# exit -worker-1$ exit -# configure worker-2 to mount the share from worker-1 -``` -```shell -$ ssh -i `pwd`/generated/instances_id_rsa opc@IP2 -``` -``` -worker-2$ sudo su - -worker-2# yum install -y nfs-utils -worker-2# mkdir /scratch -worker-2# echo "PRIVATE_IP1:/scratch /scratch nfs nfsvers=3 0 0" >> /etc/fstab -worker-2# mount /scratch -worker-2# exit -worker-2$ exit -``` +1. Verify that the cluster is accessible: + ```shell + $ kubectl get nodes + ``` ### Install Kubernetes on your own compute resources From e83a0569c366361a4f9365453de59bd08a9b6501 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Fri, 17 May 2024 18:00:02 +0000 Subject: [PATCH 066/356] catch NullPointerException in WKO tests in release/4.2 --- .../ItConfigDistributionStrategy.java | 21 +- .../kubernetes/ItEvictedPodsCycling.java | 7 +- .../kubernetes/ItIntrospectVersion.java | 6 +- .../kubernetes/ItLargeMiiDomainsClusters.java | 3 +- .../ItLiftAndShiftFromOnPremDomain.java | 15 +- .../kubernetes/ItManageNameSpace.java | 1 + .../kubernetes/ItMiiAuxiliaryImage.java | 5 +- .../kubernetes/ItMiiDomainModelInPV.java | 4 + .../kubernetes/ItMiiUpdateDomainConfig.java | 6 +- .../ItMonitoringExporterSamples.java | 12 +- .../kubernetes/ItOCILoadBalancer.java | 7 +- .../weblogic/kubernetes/ItPodTemplates.java | 5 +- .../weblogic/kubernetes/ItPodsRestart.java | 6 +- .../kubernetes/ItSessionMigration.java | 4 +- .../ItUsabilityOperatorHelmChart.java | 10 +- .../oracle/weblogic/kubernetes/ItWseeSSO.java | 2 + .../kubernetes/actions/impl/Domain.java | 11 +- .../kubernetes/utils/ApplicationUtils.java | 8 +- .../kubernetes/utils/BuildApplication.java | 10 +- .../kubernetes/utils/CleanupUtil.java | 424 ++++++++++++------ .../kubernetes/utils/ClusterUtils.java | 21 +- .../kubernetes/utils/CommonMiiTestUtils.java | 25 +- .../kubernetes/utils/CommonTestUtils.java | 34 +- .../kubernetes/utils/ConfigMapUtils.java | 33 +- .../weblogic/kubernetes/utils/DbUtils.java | 46 +- .../weblogic/kubernetes/utils/DeployUtil.java | 14 +- .../kubernetes/utils/DomainUtils.java | 8 +- .../weblogic/kubernetes/utils/FileUtils.java | 34 +- .../weblogic/kubernetes/utils/FmwUtils.java | 10 +- .../weblogic/kubernetes/utils/ImageUtils.java | 7 +- .../weblogic/kubernetes/utils/IstioUtils.java | 41 +- .../weblogic/kubernetes/utils/JobUtils.java | 6 +- .../weblogic/kubernetes/utils/K8sEvents.java | 2 +- .../kubernetes/utils/LoadBalancerUtils.java | 1 - .../kubernetes/utils/LoggingUtil.java | 56 +-- .../kubernetes/utils/MonitoringUtils.java | 5 +- .../kubernetes/utils/MySQLDBUtils.java | 9 +- .../utils/PersistentVolumeUtils.java | 24 +- .../weblogic/kubernetes/utils/PodUtils.java | 9 +- .../kubernetes/utils/SecretUtils.java | 4 +- .../kubernetes/utils/VerrazzanoUtils.java | 9 +- .../weblogic/kubernetes/utils/WLSTUtils.java | 12 +- 42 files changed, 573 insertions(+), 404 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index a2bce3a19b0..c1e3fc221fd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -207,10 +207,15 @@ public void initAll(@Namespaces(2) List namespaces) throws ApiException, //start two MySQL database instances String dbService1 = createMySQLDB("mysqldb-1", "root", "root123", domainNamespace, null); V1Pod pod = getPod(domainNamespace, null, "mysqldb-1"); + assertNotNull(pod, "pod is null"); + assertNotNull(pod.getMetadata(), "pod metadata is null"); createFileInPod(pod.getMetadata().getName(), domainNamespace, "root123"); runMysqlInsidePod(pod.getMetadata().getName(), domainNamespace, "root123"); + String dbService2 = createMySQLDB("mysqldb-2", "root", "root456", domainNamespace, null); pod = getPod(domainNamespace, null, "mysqldb-2"); + assertNotNull(pod, "pod is null"); + assertNotNull(pod.getMetadata(), "pod metadata is null"); createFileInPod(pod.getMetadata().getName(), domainNamespace, "root456"); runMysqlInsidePod(pod.getMetadata().getName(), domainNamespace, "root456"); @@ -404,8 +409,9 @@ void testModifiedOverrideContent() { testUntil( withLongRetryPolicy, - () -> listConfigMaps(domainNamespace).getItems().stream().noneMatch((cm) - -> (cm.getMetadata().getName().equals(overridecm))), + () -> listConfigMaps(domainNamespace).getItems().stream().noneMatch(cm + -> cm.getMetadata() != null && cm.getMetadata().getName() != null + && cm.getMetadata().getName().equals(overridecm)), logger, "configmap {0} to be deleted."); @@ -1195,7 +1201,10 @@ private static Integer getMySQLNodePort(String namespace, String dbName) { logger.info(dump(Kubernetes.listServices(namespace))); List services = listServices(namespace).getItems(); for (V1Service service : services) { - if (service.getMetadata().getName().startsWith(dbName)) { + if (service.getMetadata() != null && service.getMetadata().getName() != null + && service.getSpec() != null && service.getSpec().getPorts() != null + && !service.getSpec().getPorts().isEmpty() + && service.getMetadata().getName().startsWith(dbName)) { return service.getSpec().getPorts().get(0).getNodePort(); } } @@ -1206,7 +1215,8 @@ private static String getMySQLSvcName(String namespace, String dbName) { logger.info(dump(Kubernetes.listServices(namespace))); List services = listServices(namespace).getItems(); for (V1Service service : services) { - if (service.getMetadata().getName().startsWith(dbName)) { + if (service.getMetadata() != null && service.getMetadata().getName() != null + && service.getMetadata().getName().startsWith(dbName)) { return service.getMetadata().getName(); } } @@ -1227,7 +1237,8 @@ private static String getMySQLSvcEndpoint(String domainNamespace, String dbName) } catch (Exception e) { getLogger().info("Got exception, command failed with errors " + e.getMessage()); } - return result.stdout(); + assertNotNull(result, "result is null"); + return result.stdout(); } private static void runMysqlInsidePod(String podName, String namespace, String password) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java index e57542270ac..f0e05b16ce2 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java @@ -204,9 +204,14 @@ private Callable checkEvictionEvent(String adminServerpodName, boolean gotEvent = false; List events = Kubernetes.listNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - if (event.getType().equals(type) + if (event.getType() != null + && event.getType().equals(type) + && event.getInvolvedObject() != null + && event.getInvolvedObject().getName() != null && event.getInvolvedObject().getName().equals(adminServerpodName) + && event.getReason() != null && event.getReason().equals(reason) + && event.getMessage() != null && event.getMessage().contains(message)) { logger.info(Yaml.dump(event)); gotEvent = true; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index 1cbcc44fc14..e14f726b5d6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -1351,6 +1351,7 @@ private static void verifyMemberHealth(String adminServerPodName, List m } catch (IOException | InterruptedException ex) { logger.severe(ex.getMessage()); } + assertNotNull(result, "result is null"); String response = result.stdout().trim(); logger.info(response); logger.info(result.stderr()); @@ -1574,7 +1575,10 @@ private DomainResource createDomainResourceWithConfigMap(String domainUid, Strin private void updateIngressBackendServicePort(int newAdminPort) throws ApiException { String ingressName = introDomainNamespace + "-" + domainUid + "-" + adminServerName + "-7001"; V1Ingress ingress = Ingress.getIngress(introDomainNamespace, ingressName).orElse(null); - if (ingress != null) { + if (ingress != null + && ingress.getSpec() != null + && ingress.getSpec().getRules() != null + && !ingress.getSpec().getRules().isEmpty()) { logger.info("Updating ingress {0} with new admin port {1}", ingressName, newAdminPort); ingress.getSpec().getRules().getFirst().getHttp() .getPaths().getFirst().getBackend().getService() diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLargeMiiDomainsClusters.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLargeMiiDomainsClusters.java index cc6e972b5f5..2c050f45986 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLargeMiiDomainsClusters.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLargeMiiDomainsClusters.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -102,7 +102,6 @@ import static oracle.weblogic.kubernetes.utils.K8sEvents.checkDomainEvent; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OKDUtils.setTlsTerminationForRoute; -import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResource; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.getPodsWithTimeStamps; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java index 3db77b3e65b..08b410ed9ee 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java @@ -109,7 +109,6 @@ @IntegrationTest class ItLiftAndShiftFromOnPremDomain { - private static String opNamespace = null; private static String traefikNamespace = null; private static String domainNamespace = null; private static final String LIFT_AND_SHIFT_WORK_DIR = WORK_DIR + "/liftandshiftworkdir"; @@ -124,10 +123,8 @@ class ItLiftAndShiftFromOnPremDomain { private static final Path BUILD_SCRIPT_SOURCE_PATH = Paths.get(RESOURCE_DIR, "bash-scripts", BUILD_SCRIPT); private static final String domainUid = "onprem-domain"; private static final String adminServerName = "admin-server"; - private static final String appPath = "opdemo/index.jsp"; private static String imageName = null; private static LoggingFacade logger = null; - private Path zipFile; private static HelmParams traefikHelmParams = null; private int traefikNodePort = 0; @@ -145,7 +142,7 @@ public static void initAll(@Namespaces(3) List namespaces) { // get a new unique opNamespace logger.info("Creating unique namespace for Operator"); assertNotNull(namespaces.get(0), "Namespace list is null"); - opNamespace = namespaces.get(0); + String opNamespace = namespaces.get(0); // get a unique traefik namespace logger.info("Get a unique namespace for traefik"); @@ -220,13 +217,17 @@ void testCreateMiiDomainWithClusterFromOnPremDomain() throws UnknownHostExceptio } Path tempDomainDir = Paths.get(DOMAIN_TEMP_DIR); - zipFile = Paths.get(createZipFile(tempDomainDir)); + String tmpDomainDirZip = createZipFile(tempDomainDir); + assertNotNull(tmpDomainDirZip, String.format("failed to create zip file %s", DOMAIN_TEMP_DIR)); + Path zipFile = Paths.get(tmpDomainDirZip); logger.info("zipfile is in {0}", zipFile.toString()); // Call WDT DiscoverDomain tool with wko target to get the required file to create a // Mii domain image. Since WDT requires weblogic installation, we start a pod and run // wdt discoverDomain tool in the pod V1Pod webLogicPod = callSetupWebLogicPod(domainNamespace); + assertNotNull(webLogicPod, "webLogicPod is null"); + assertNotNull(webLogicPod.getMetadata(), "webLogicPod metadata is null"); // copy the onprem domain zip file to /u01 location inside pod try { @@ -351,7 +352,7 @@ void testCreateMiiDomainWithClusterFromOnPremDomain() throws UnknownHostExceptio } } - String hostAndPort = null; + String hostAndPort; if (OKD) { hostAndPort = getHostAndPort(hostName, traefikNodePort); } else { @@ -378,7 +379,7 @@ void testCreateMiiDomainWithClusterFromOnPremDomain() throws UnknownHostExceptio "opdemo/index.jsp", "WebLogic on prem to wko App"); - ExecResult execResult = null; + ExecResult execResult; logger.info("curl command {0}", curlString); execResult = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java index ff91078cc96..a5a442f65f7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java @@ -439,6 +439,7 @@ private static void setLabelToNamespace(String domainNS, Map lab //add label to domain namespace V1Namespace namespaceObject1 = assertDoesNotThrow(() -> Kubernetes.getNamespace(domainNS)); assertNotNull(namespaceObject1, "Can't find namespace with name " + domainNS); + assertNotNull(namespaceObject1.getMetadata(), "namespaceObject metadata is null"); namespaceObject1.getMetadata().setLabels(labels); assertDoesNotThrow(() -> Kubernetes.replaceNamespace(namespaceObject1)); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index 115b149ed17..cac5bbceac4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -850,8 +850,9 @@ WEBLOGIC_IMAGE_TO_USE_IN_SPEC, adminSecretName, createSecretsForImageRepos(domai deleteConfigMap(configMapName, domainNamespace); testUntil( withLongRetryPolicy, - () -> listConfigMaps(domainNamespace).getItems().stream().noneMatch((cm) - -> (cm.getMetadata().getName().equals(configMapName))), + () -> listConfigMaps(domainNamespace).getItems().stream().noneMatch(cm + -> cm.getMetadata() != null && cm.getMetadata().getName() != null + && cm.getMetadata().getName().equals(configMapName)), logger, "configmap {0} to be deleted.", configMapName); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java index ed9332d2256..b5ce1476815 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainModelInPV.java @@ -202,6 +202,8 @@ public static void initAll(@Namespaces(2) List namespaces) { logger.info("Setting up WebLogic pod to access PV"); V1Pod pvPod = setupWebLogicPod(domainNamespace); + assertNotNull(pvPod, "pvPod is null"); + assertNotNull(pvPod.getMetadata(), "pvPod metadata is null"); logger.info("Creating directory {0} in PV", modelMountPath + "/applications"); execInPod(pvPod, null, true, "mkdir -p " + modelMountPath + "/applications"); @@ -346,6 +348,7 @@ private static void verifyMemberHealth(String adminServerPodName, List m } catch (IOException | InterruptedException ex) { logger.severe(ex.getMessage()); } + assertNotNull(result, "execResult is null"); String response = result.stdout().trim(); logger.info(response); boolean health = true; @@ -398,6 +401,7 @@ private static void verifyMemberHealth(String adminServerPodName, List m } boolean health = true; + assertNotNull(result, "result is null"); for (String managedServer : managedServerNames) { health = health && result.stdout().contains(managedServer + ":HEALTH_OK"); if (health) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index c1733cdaddb..02ccb1a7d30 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -268,8 +268,8 @@ void testMiiCustomEnv() { for (int i = 0; i < envList.size(); i++) { logger.info("The name is: {0}, value is: {1}", envList.get(i).getName(), envList.get(i).getValue()); if (envList.get(i).getName().equalsIgnoreCase("CUSTOM_ENV")) { - assertTrue( - envList.get(i).getValue().equalsIgnoreCase("${DOMAIN_UID}~##!'%*$(ls)"), + assertTrue(envList.get(i).getValue() != null + && envList.get(i).getValue().equalsIgnoreCase("${DOMAIN_UID}~##!'%*$(ls)"), "Expected value for CUSTOM_ENV variable does not mtach"); found = true; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java index 90ebdc7b745..7bed8798952 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java @@ -295,6 +295,8 @@ public static void initAll(@Namespaces(6) List namespaces) { dbService = createMySQLDB("mysql", "root", "root123", domain2Namespace, null); assertNotNull(dbService, "Failed to create database"); V1Pod pod = getPod(domain2Namespace, null, "mysql"); + assertNotNull(pod, "pod is null"); + assertNotNull(pod.getMetadata(), "pod metadata is null"); createFileInPod(pod.getMetadata().getName(), domain2Namespace, "root123"); runMysqlInsidePod(pod.getMetadata().getName(), domain2Namespace, "root123", "/tmp/grant.sql"); runMysqlInsidePod(pod.getMetadata().getName(), domain2Namespace, "root123", "/tmp/create.sql"); @@ -390,10 +392,8 @@ private void fireAlert() throws ApiException { List pods = listPods(webhookNS, "app=webhook").getItems(); assertNotNull((pods), "No pods are running in namespace : " + webhookNS); V1Pod pod = pods.stream() - .filter(testpod -> testpod - .getMetadata() - .getName() - .contains("webhook")) + .filter(testpod -> testpod.getMetadata() != null && testpod.getMetadata().getName() != null + && testpod.getMetadata().getName().contains("webhook")) .findAny() .orElse(null); @@ -706,7 +706,7 @@ private static void uninstallDeploymentService(V1Deployment deployment, V1Servic String serviceName = null; String deploymentName = null; try { - if (service != null) { + if (service != null && service.getMetadata() != null) { serviceName = service.getMetadata().getName(); namespace = service.getMetadata().getNamespace(); Kubernetes.deleteService(serviceName, namespace); @@ -717,7 +717,7 @@ private static void uninstallDeploymentService(V1Deployment deployment, V1Servic serviceName, namespace); } try { - if (deployment != null) { + if (deployment != null && deployment.getMetadata() != null) { deploymentName = deployment.getMetadata().getName(); namespace = deployment.getMetadata().getNamespace(); Kubernetes.deleteDeployment(namespace, deploymentName); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java index b859da6e8e1..799158e10fd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java @@ -133,12 +133,13 @@ private static String getLoadBalancerIP(String namespace, String lbName) throws V1Service service = getService(lbName, labels, namespace); assertNotNull(service, "Can't find service with name " + lbName); logger.info("Found service with name {0} in {1} namespace ", lbName, namespace); + assertNotNull(service.getStatus(), "service status is null"); + assertNotNull(service.getStatus().getLoadBalancer(), "service loadbalancer is null"); List ingress = service.getStatus().getLoadBalancer().getIngress(); if (ingress != null) { logger.info("LoadBalancer Ingress " + ingress.toString()); - V1LoadBalancerIngress lbIng = ingress.stream().filter(c -> - !c.getIp().equals("pending") - ).findAny().orElse(null); + V1LoadBalancerIngress lbIng = + ingress.stream().filter(c -> c.getIp() != null && !c.getIp().equals("pending")).findAny().orElse(null); if (lbIng != null) { logger.info("OCI LoadBalancer is created with external ip" + lbIng.getIp()); return lbIng.getIp(); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java index 32f6f4f980b..eb356a32931 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -139,6 +139,8 @@ private void createAndVerifyPodFromTemplate(String imageName, //check that managed server pod is up and all applicable variable values are initialized. assertNotNull(managedServerPod,"The managed server pod does not exist in namespace " + domainNamespace); V1ObjectMeta managedServerMetadata = managedServerPod.getMetadata(); + assertNotNull(managedServerMetadata, "managed server pod metadata is null"); + assertNotNull(managedServerMetadata.getLabels(), "managed server metadata label is null"); String serverName = managedServerMetadata.getLabels().get("servername"); logger.info("Checking that variables used in the labels and annotations " + "in the serverPod for servername, domainname, clustername are initialized"); @@ -170,6 +172,7 @@ private void createAndVerifyPodFromTemplate(String imageName, logger.info("Checking that applicable variables used " + "in the annotations for domainhome and loghome are initialized"); + assertNotNull(managedServerMetadata.getAnnotations(), "managed server metadata annotation is null"); String loghome = managedServerMetadata.getAnnotations().get("loghome"); //check that annotation contains loghome in the pod assertNotNull(loghome, "Can't find annotation loghome"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java index 22bc20061e0..5be97ce5adb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -375,8 +375,8 @@ void testServerPodsRestartByChangingEnvProperty() { envList = domain1.getSpec().getServerPod().getEnv(); String envValue = envList.get(0).getValue(); logger.info("In the new patched domain envValue is: {0}", envValue); - assertTrue(envValue.equalsIgnoreCase("-Dweblogic.StdoutDebugEnabled=true"), "JAVA_OPTIONS was not updated" - + " in the new patched domain"); + assertTrue(envValue != null && envValue.equalsIgnoreCase("-Dweblogic.StdoutDebugEnabled=true"), + "JAVA_OPTIONS was not updated in the new patched domain"); // verify the server pods are rolling restarted and back to ready state logger.info("Verifying rolling restart occurred for domain {0} in namespace {1}", diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java index 7ccdcf174df..bf834c63aec 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -246,6 +246,8 @@ void testPodAnnotationWithSlash() { assertNotNull(managedServerPod, "The managed server pod does not exist in namespace " + domainNamespace); V1ObjectMeta managedServerMetadata = managedServerPod.getMetadata(); + assertNotNull(managedServerMetadata, "managed server metadata is null"); + assertNotNull(managedServerMetadata.getAnnotations(), "managed server metadata annotation is null"); String myAnnotationValue = managedServerMetadata.getAnnotations().get(annotationKey); String myAnnotationValue2 = managedServerMetadata.getAnnotations().get(annotationKey2); String myAnnotationValue3 = managedServerMetadata.getAnnotations().get(annotationKey3); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java index 3b05b65b3ca..cc04c5204ea 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java @@ -1246,6 +1246,7 @@ private static String getExecError(String command) { return result.stderr(); } } catch (Exception e) { + assertNotNull(result, "result is null"); getLogger().info("Got exception, command failed with errors " + e.getMessage()); return result.stderr(); } @@ -1256,9 +1257,11 @@ private void cleanUpSA(String namespace) { V1ServiceAccountList sas = Kubernetes.listServiceAccounts(namespace); if (sas != null) { for (V1ServiceAccount sa : sas.getItems()) { - String saName = sa.getMetadata().getName(); - deleteServiceAccount(saName, namespace); - checkServiceDoesNotExist(saName, namespace); + if (sa.getMetadata() != null) { + String saName = sa.getMetadata().getName(); + deleteServiceAccount(saName, namespace); + checkServiceDoesNotExist(saName, namespace); + } } } } @@ -1270,7 +1273,6 @@ private void cleanUpSA(String namespace) { **/ private boolean checkManagedServerConfiguration(String domainNamespace, String domainUid) throws UnknownHostException { - ExecResult result; String adminServerPodName = domainUid + adminServerPrefix; String managedServer = "managed-server1"; int adminServiceNodePort diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java index d23f9b56d5f..5865ba17f5b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java @@ -388,6 +388,8 @@ private static void copyKeyStores(String domainNamespace, logger.info("Setting up WebLogic pod to access PV"); V1Pod pvPod = setupWebLogicPod(domainNamespace, pvName, pvcName, "/shared"); + assertNotNull(pvPod, "pvPod is null"); + assertNotNull(pvPod.getMetadata(), "pvPod metadata is null"); logger.info("Creating directory {0} in PV", jksMountPath); execInPod(pvPod, null, true, "mkdir -p " + jksMountPath); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java index 7792a7dbfd4..0f3696885f0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Domain.java @@ -428,7 +428,7 @@ public static boolean scaleClusterWithRestApi(String domainUid, logger.info("Getting service account token stored in secret {0} to authenticate as service account {1}" + " in namespace {2}", secretName, opServiceAccount, opNamespace); String secretToken = Secret.getSecretEncodedToken(opNamespace, secretName); - if (secretToken.isEmpty()) { + if (secretToken == null || secretToken.isEmpty()) { logger.info("Did not get encoded token for secret {0} associated with service account {1} in namespace {2}", secretName, opServiceAccount, opNamespace); return false; @@ -504,7 +504,7 @@ public static boolean scaleClusterWithRestApiInOpPod(String domainUid, logger.info("Getting the secret of service account {0} in namespace {1}", opServiceAccount, opNamespace); String secretName = Secret.getSecretOfServiceAccount(opNamespace, opServiceAccount); - if (secretName.isEmpty()) { + if (secretName == null || secretName.isEmpty()) { logger.info("Did not find secret of service account {0} in namespace {1}", opServiceAccount, opNamespace); return false; } @@ -513,7 +513,7 @@ public static boolean scaleClusterWithRestApiInOpPod(String domainUid, logger.info("Getting service account token stored in secret {0} to authenticate as service account {1}" + " in namespace {2}", secretName, opServiceAccount, opNamespace); String secretToken = Secret.getSecretEncodedToken(opNamespace, secretName); - if (secretToken.isEmpty()) { + if (secretToken == null || secretToken.isEmpty()) { logger.info("Did not get encoded token for secret {0} associated with service account {1} in namespace {2}", secretName, opServiceAccount, opNamespace); return false; @@ -595,7 +595,7 @@ public static ExecResult scaleClusterWithRestApiAndReturnResult(String domainUid logger.info("Getting service account token stored in secret {0} to authenticate as service account {1}" + " in namespace {2}", secretName, opServiceAccount, opNamespace); String secretToken = Secret.getSecretEncodedToken(opNamespace, secretName); - if (secretToken.isEmpty()) { + if (secretToken == null || secretToken.isEmpty()) { logger.info("Did not get encoded token for secret {0} associated with service account {1} in namespace {2}", secretName, opServiceAccount, opNamespace); return new ExecResult(12, "", "secret token is empty"); @@ -1126,6 +1126,8 @@ private static void scaleViaScript(String opNamespace, String domainNamespace, String commandToExecuteInsidePod = scalingCommand.toString(); ExecResult result = null; + assertNotNull(adminPod, "admin pod is null"); + assertNotNull(adminPod.getMetadata(), "admin pod metadata is null"); try { result = assertDoesNotThrow(() -> Kubernetes.exec(adminPod, null, true, "/bin/sh", "-c", commandToExecuteInsidePod), @@ -1134,6 +1136,7 @@ private static void scaleViaScript(String opNamespace, String domainNamespace, logger.info("Command {0} returned with exit value {1}, stderr {2}, stdout {3}", commandToExecuteInsidePod, result.exitValue(), result.stderr(), result.stdout()); } catch (Error err) { + assertNotNull(result, "result is null"); logger.info("Command {0} returned with exit value {1}, stderr {2}, stdout {3}", commandToExecuteInsidePod, result.exitValue(), result.stderr(), result.stdout()); // copy scalingAction.log to local diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java index a3d434d9b13..300f365f113 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java @@ -36,16 +36,16 @@ public class ApplicationUtils { * @return true if curl command returns HTTP code 200 otherwise false */ public static boolean checkAppUsingHostHeader(String url, String hostHeader, Boolean... args) { - boolean checlReadyAppAccessible = (args.length == 0) ? true : false; + boolean checlReadyAppAccessible = args.length == 0; LoggingFacade logger = getLogger(); StringBuffer curlString = new StringBuffer("status=$(curl --user weblogic:welcome1 "); - StringBuffer headerString = null; + StringBuffer headerString; if (hostHeader != null) { headerString = new StringBuffer("-H 'host: "); headerString.append(hostHeader) .append("' "); } else { - headerString = new StringBuffer(""); + headerString = new StringBuffer(); } curlString.append(" -g -sk --noproxy '*' ") .append(" --silent --show-error ") @@ -426,7 +426,7 @@ public static void verifyAdminConsoleAccessible(String domainNamespace, String port, boolean secureMode, Boolean... args) { - boolean checlReadyAppAccessible = (args.length == 0) ? true : false; + boolean checlReadyAppAccessible = args.length == 0; LoggingFacade logger = getLogger(); String httpKey = "http://"; if (secureMode) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/BuildApplication.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/BuildApplication.java index adef2e0b001..616b4155074 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/BuildApplication.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/BuildApplication.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -37,6 +37,7 @@ import static org.apache.commons.io.FileUtils.copyDirectory; import static org.apache.commons.io.FileUtils.deleteDirectory; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** @@ -117,7 +118,7 @@ public static Path buildApplication( Path destArchiveBaseDir = targetPath == null ? Paths.get(WORK_DIR, appSrcPath.getFileName().toString()) : targetPath; logger.info("DestArchiveBaseDir set to {0}", destArchiveBaseDir.toString()); - Path destDir = null; + assertDoesNotThrow(() -> { // recreate WORK_DIR/j2eeapplications/ @@ -138,7 +139,6 @@ public static Path buildApplication( // zip up the application source to be copied to pod for building Path zipFile = Paths.get(FileUtils.createZipFile(tempAppPath)); - // add ant properties as env variable in pod V1Container buildContainer = new V1Container(); @@ -165,6 +165,8 @@ public static Path buildApplication( //setup temporary WebLogic pod to build application V1Pod webLogicPod = setupWebLogicPod(namespace, buildContainer); + assertNotNull(webLogicPod, "webLogicPod is null"); + assertNotNull(webLogicPod.getMetadata(), "webLogicPod metadata is null"); try { //copy the zip file to /u01 location inside pod @@ -221,7 +223,7 @@ public static Path buildApplication( logger.info("Exception while copying file " + Paths.get(APPLICATIONS_PATH, archiveDistDir) + " from pod", ioex); } - return destDir = Paths.get(destArchiveBaseDir.toString(), "u01/application", archiveDistDir); + return Paths.get(destArchiveBaseDir.toString(), "u01/application", archiveDistDir); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java index dfad0b89bbb..3277b7f76d3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CleanupUtil.java @@ -10,18 +10,29 @@ import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1ConfigMapList; import io.kubernetes.client.openapi.models.V1Deployment; +import io.kubernetes.client.openapi.models.V1DeploymentList; import io.kubernetes.client.openapi.models.V1Ingress; +import io.kubernetes.client.openapi.models.V1IngressList; import io.kubernetes.client.openapi.models.V1Job; import io.kubernetes.client.openapi.models.V1JobList; import io.kubernetes.client.openapi.models.V1PersistentVolume; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim; +import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimList; import io.kubernetes.client.openapi.models.V1ReplicaSet; +import io.kubernetes.client.openapi.models.V1ReplicaSetList; import io.kubernetes.client.openapi.models.V1Role; import io.kubernetes.client.openapi.models.V1RoleBinding; +import io.kubernetes.client.openapi.models.V1RoleBindingList; +import io.kubernetes.client.openapi.models.V1RoleList; import io.kubernetes.client.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1SecretList; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServiceAccount; +import io.kubernetes.client.openapi.models.V1ServiceAccountList; +import io.kubernetes.client.openapi.models.V1ServiceList; +import oracle.weblogic.domain.DomainList; import oracle.weblogic.domain.DomainResource; import oracle.weblogic.kubernetes.TestConstants; import oracle.weblogic.kubernetes.actions.TestActions; @@ -30,7 +41,32 @@ import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteConfigMap; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteDeployment; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteJob; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteNamespacedRole; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteNamespacedRoleBinding; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deletePv; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deletePvc; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteReplicaSet; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteSecret; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteService; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.deleteServiceAccount; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listConfigMaps; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listDeployments; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listDomains; import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listJobs; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listNamespacedIngresses; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listNamespacedRoleBinding; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listNamespacedRoles; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listNamespaces; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listPersistentVolumeClaims; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listPersistentVolumes; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listReplicaSets; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listSecrets; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listServiceAccounts; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listServices; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -132,14 +168,20 @@ private static void deleteClusters(String namespace) { */ private static void deleteDomains(String namespace) { LoggingFacade logger = getLogger(); - try { - for (var item : Kubernetes.listDomains(namespace).getItems()) { - String domainUid = item.getMetadata().getName(); - Kubernetes.deleteDomainCustomResource(domainUid, namespace); + DomainList domainList = listDomains(namespace); + if (domainList != null) { + List items = domainList.getItems(); + try { + for (var item : items) { + if (item.getMetadata() != null) { + String domainUid = item.getMetadata().getName(); + deleteDomainCustomResource(domainUid, namespace); + } + } + } catch (Exception ex) { + logger.severe(ex.getMessage()); + logger.severe("Failed to delete domain or the {0} is not a domain namespace", namespace); } - } catch (Exception ex) { - logger.severe(ex.getMessage()); - logger.severe("Failed to delete domain or the {0} is not a domain namespace", namespace); } } @@ -182,13 +224,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // Check if any domains exist try { - if (!Kubernetes.listDomains(namespace).getItems().isEmpty()) { - logger.info("Domain still exists !!!"); - List items = Kubernetes.listDomains(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + DomainList domainList = listDomains(namespace); + if (domainList != null) { + List items = domainList.getItems(); + if (!items.isEmpty()) { + logger.info("Domain still exists !!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -197,13 +244,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // Check if any replica sets exist try { - if (!Kubernetes.listReplicaSets(namespace).getItems().isEmpty()) { - logger.info("ReplicaSets still exists!!!"); - List items = Kubernetes.listReplicaSets(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1ReplicaSetList replicaSetList = listReplicaSets(namespace); + if (replicaSetList != null) { + List items = replicaSetList.getItems(); + if (!items.isEmpty()) { + logger.info("ReplicaSets still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -232,13 +284,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any configmaps exist try { - if (!Kubernetes.listConfigMaps(namespace).getItems().isEmpty()) { - logger.info("Config Maps still exists!!!"); - List items = Kubernetes.listConfigMaps(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1ConfigMapList configMapList = listConfigMaps(namespace); + if (configMapList != null) { + List items = configMapList.getItems(); + if (!items.isEmpty()) { + logger.info("Config Maps still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -247,13 +304,17 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any secrets exist try { - if (!Kubernetes.listSecrets(namespace).getItems().isEmpty()) { - logger.info("Secrets still exists!!!"); - List items = Kubernetes.listSecrets(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1SecretList secretList = listSecrets(namespace); + if (secretList != null) { + List items = secretList.getItems(); + if (!items.isEmpty()) { + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -262,13 +323,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any persistent volume claims exist try { - if (!Kubernetes.listPersistentVolumeClaims(namespace).getItems().isEmpty()) { - logger.info("Persistent Volumes Claims still exists!!!"); - List items = Kubernetes.listPersistentVolumeClaims(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1PersistentVolumeClaimList pvcList = listPersistentVolumeClaims(namespace); + if (pvcList != null) { + List items = pvcList.getItems(); + if (!items.isEmpty()) { + logger.info("Persistent Volumes Claims still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -277,23 +343,29 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any persistent volumes exist try { - for (var item : Kubernetes.listPersistentVolumeClaims(namespace).getItems()) { - String label = Optional.ofNullable(item) - .map(pvc -> pvc.getMetadata()) - .map(metadata -> metadata.getLabels()) - .map(labels -> labels.get("weblogic.domainUid")).get(); - - if (!Kubernetes.listPersistentVolumes( - String.format("weblogic.domainUid = %s", label)) - .getItems().isEmpty()) { - logger.info("Persistent Volumes still exists!!!"); - List pvs = Kubernetes.listPersistentVolumes( + V1PersistentVolumeClaimList persistentVolumeClaimList = listPersistentVolumeClaims(namespace); + if (persistentVolumeClaimList != null) { + List items = persistentVolumeClaimList.getItems(); + for (var item : items) { + String label = Optional.ofNullable(item) + .map(pvc -> pvc.getMetadata()) + .map(metadata -> metadata.getLabels()) + .map(labels -> labels.get("weblogic.domainUid")).get(); + + if (!listPersistentVolumes( String.format("weblogic.domainUid = %s", label)) - .getItems(); - for (var pv : pvs) { - logger.info(pv.getMetadata().getName()); + .getItems().isEmpty()) { + logger.info("Persistent Volumes still exists!!!"); + List pvs = listPersistentVolumes( + String.format("weblogic.domainUid = %s", label)) + .getItems(); + for (var pv : pvs) { + if (pv.getMetadata() != null) { + logger.info(pv.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } } catch (Exception ex) { @@ -303,13 +375,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any deployments exist try { - if (!Kubernetes.listDeployments(namespace).getItems().isEmpty()) { - logger.info("Deployments still exists!!!"); - List items = Kubernetes.listDeployments(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1DeploymentList deploymentList = listDeployments(namespace); + if (deploymentList != null) { + List items = deploymentList.getItems(); + if (!items.isEmpty()) { + logger.info("Deployments still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -318,13 +395,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any services exist try { - if (!Kubernetes.listServices(namespace).getItems().isEmpty()) { - logger.info("Services still exists!!!"); - List items = Kubernetes.listServices(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1ServiceList serviceList = listServices(namespace); + if (serviceList != null) { + List items = serviceList.getItems(); + if (!items.isEmpty()) { + logger.info("Services still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -333,13 +415,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any service accounts exist try { - if (!Kubernetes.listServiceAccounts(namespace).getItems().isEmpty()) { - logger.info("Service Accounts still exists!!!"); - List items = Kubernetes.listServiceAccounts(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1ServiceAccountList serviceAccountList = listServiceAccounts(namespace); + if (serviceAccountList != null) { + List items = serviceAccountList.getItems(); + if (!items.isEmpty()) { + logger.info("Service Accounts still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -348,13 +435,17 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any ingress exist try { - if (!Kubernetes.listNamespacedIngresses(namespace).getItems().isEmpty()) { - logger.info("Ingresses still exists!!!"); - List items = Kubernetes.listNamespacedIngresses(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1IngressList ingressList = listNamespacedIngresses(namespace); + if (ingressList != null) { + List items = listNamespacedIngresses(namespace).getItems(); + if (!items.isEmpty()) { + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -363,13 +454,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any namespaced roles exist try { - if (!Kubernetes.listNamespacedRoles(namespace).getItems().isEmpty()) { - logger.info("Namespaced roles still exists!!!"); - List items = Kubernetes.listNamespacedRoles(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1RoleList roleList = listNamespacedRoles(namespace); + if (roleList != null) { + List items = roleList.getItems(); + if (!items.isEmpty()) { + logger.info("Namespaced roles still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -378,13 +474,18 @@ public static Callable nothingFoundInNamespace(String namespace) { // check if any namespaced role bindings exist try { - if (!Kubernetes.listNamespacedRoleBinding(namespace).getItems().isEmpty()) { - logger.info("Namespaced role bindings still exists!!!"); - List items = Kubernetes.listNamespacedRoleBinding(namespace).getItems(); - for (var item : items) { - logger.info(item.getMetadata().getName()); + V1RoleBindingList roleBindingList = listNamespacedRoleBinding(namespace); + if (roleBindingList != null) { + List items = roleBindingList.getItems(); + if (!items.isEmpty()) { + logger.info("Namespaced role bindings still exists!!!"); + for (var item : items) { + if (item.getMetadata() != null) { + logger.info(item.getMetadata().getName()); + } + } + nothingFound = false; } - nothingFound = false; } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -408,7 +509,7 @@ public static Callable namespaceNotFound(String namespace) { boolean notFound = true; // get namespaces try { - List namespaceList = Kubernetes.listNamespaces(); + List namespaceList = listNamespaces(); if (namespaceList.contains(namespace)) { logger.info("Namespace still exists!!!"); logger.info(namespace); @@ -433,8 +534,14 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete all Domain objects in the given namespace try { - for (var item : Kubernetes.listDomains(namespace).getItems()) { - Kubernetes.deleteDomainCustomResource(item.getMetadata().getName(), namespace); + DomainList domainList = listDomains(namespace); + if (domainList != null) { + List items = domainList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteDomainCustomResource(item.getMetadata().getName(), namespace); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -443,8 +550,14 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete deployments try { - for (var item : Kubernetes.listDeployments(namespace).getItems()) { - Kubernetes.deleteDeployment(namespace, item.getMetadata().getName()); + V1DeploymentList deploymentList = listDeployments(namespace); + if (deploymentList != null) { + List items = deploymentList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteDeployment(namespace, item.getMetadata().getName()); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -453,8 +566,14 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete replicasets try { - for (var item : Kubernetes.listReplicaSets(namespace).getItems()) { - Kubernetes.deleteReplicaSet(namespace, item.getMetadata().getName()); + V1ReplicaSetList replicaSetList = listReplicaSets(namespace); + if (replicaSetList != null) { + List items = replicaSetList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteReplicaSet(namespace, item.getMetadata().getName()); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -467,7 +586,7 @@ public static void deleteNamespacedArtifacts(String namespace) { if (jobList != null) { for (var item : jobList.getItems()) { if (item.getMetadata() != null) { - Kubernetes.deleteJob(namespace, item.getMetadata().getName()); + deleteJob(namespace, item.getMetadata().getName()); } } } @@ -478,8 +597,15 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete configmaps try { - for (var item : Kubernetes.listConfigMaps(namespace).getItems()) { - Kubernetes.deleteConfigMap(item.getMetadata().getName(), namespace); + V1ConfigMapList configMapList = listConfigMaps(namespace); + if (configMapList != null) { + List items = configMapList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteConfigMap(item.getMetadata().getName(), namespace); + } + } + } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -488,8 +614,14 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete secrets try { - for (var item : Kubernetes.listSecrets(namespace).getItems()) { - Kubernetes.deleteSecret(item.getMetadata().getName(), namespace); + V1SecretList secretList = listSecrets(namespace); + if (secretList != null) { + List items = secretList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteSecret(item.getMetadata().getName(), namespace); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -497,43 +629,57 @@ public static void deleteNamespacedArtifacts(String namespace) { } // Delete pvc - List pvs = new ArrayList(); - - for (var pvc : Kubernetes.listPersistentVolumeClaims(namespace).getItems()) { - String label = null; - if (pvc.getMetadata().getLabels() != null) { - label = pvc.getMetadata().getLabels().get("weblogic.domainUid"); - } - // get a list of pvs used by the pvcs in this namespace - try { - if (null != label) { - List items = Kubernetes.listPersistentVolumes( - String.format("weblogic.domainUid = %s", label)).getItems(); - pvs.addAll(items); + List pvs = new ArrayList<>(); + + V1PersistentVolumeClaimList persistentVolumeClaimList = listPersistentVolumeClaims(namespace); + if (persistentVolumeClaimList != null) { + List pvcItems = persistentVolumeClaimList.getItems(); + for (var pvc : pvcItems) { + String label = null; + if (pvc.getMetadata() != null && pvc.getMetadata().getLabels() != null) { + label = pvc.getMetadata().getLabels().get("weblogic.domainUid"); + } + // get a list of pvs used by the pvcs in this namespace + try { + if (null != label) { + List items = listPersistentVolumes( + String.format("weblogic.domainUid = %s", label)).getItems(); + pvs.addAll(items); + } + // delete the pvc + if (pvc.getMetadata() != null) { + deletePvc(pvc.getMetadata().getName(), namespace); + } + } catch (ApiException ex) { + logger.warning(ex.getResponseBody()); } - // delete the pvc - Kubernetes.deletePvc(pvc.getMetadata().getName(), namespace); - } catch (ApiException ex) { - logger.warning(ex.getResponseBody()); } } // Delete pv if (!pvs.isEmpty()) { for (var item : pvs) { - try { - logger.info("Deleting persistent volume {0}", item.getMetadata().getName()); - Kubernetes.deletePv(item.getMetadata().getName()); - } catch (Exception ex) { - logger.warning("Failed to delete persistent volumes"); + if (item.getMetadata() != null) { + try { + logger.info("Deleting persistent volume {0}", item.getMetadata().getName()); + deletePv(item.getMetadata().getName()); + } catch (Exception ex) { + logger.warning("Failed to delete persistent volumes"); + } } } } // Delete services try { - for (var item : Kubernetes.listServices(namespace).getItems()) { - Kubernetes.deleteService(item.getMetadata().getName(), namespace); + V1ServiceList serviceList = listServices(namespace); + if (serviceList != null) { + List items = serviceList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteService(item.getMetadata().getName(), namespace); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -542,8 +688,13 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete namespaced roles try { - for (var item : Kubernetes.listNamespacedRoles(namespace).getItems()) { - Kubernetes.deleteNamespacedRole(namespace, item.getMetadata().getName()); + V1RoleList roleList = listNamespacedRoles(namespace); + if (roleList != null) { + for (var item : roleList.getItems()) { + if (item.getMetadata() != null) { + deleteNamespacedRole(namespace, item.getMetadata().getName()); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -552,8 +703,13 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete namespaced role bindings try { - for (var item : Kubernetes.listNamespacedRoleBinding(namespace).getItems()) { - Kubernetes.deleteNamespacedRoleBinding(namespace, item.getMetadata().getName()); + V1RoleBindingList roleBindingList = listNamespacedRoleBinding(namespace); + if (roleBindingList != null) { + for (var item : roleBindingList.getItems()) { + if (item.getMetadata() != null) { + deleteNamespacedRoleBinding(namespace, item.getMetadata().getName()); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -562,8 +718,14 @@ public static void deleteNamespacedArtifacts(String namespace) { // Delete service accounts try { - for (var item : Kubernetes.listServiceAccounts(namespace).getItems()) { - Kubernetes.deleteServiceAccount(item.getMetadata().getName(), namespace); + V1ServiceAccountList serviceAccountList = listServiceAccounts(namespace); + if (serviceAccountList != null) { + List items = serviceAccountList.getItems(); + for (var item : items) { + if (item.getMetadata() != null) { + deleteServiceAccount(item.getMetadata().getName(), namespace); + } + } } } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -583,7 +745,7 @@ public static void deleteNamespace(String namespace) { Kubernetes.deleteNamespace(namespace); } catch (Exception ex) { logger.warning(ex.getMessage()); - logger.warning("Failed to delete namespace"); + logger.warning("Failed to delete namespace {0}", namespace); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ClusterUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ClusterUtils.java index 68292ac6d25..edc3d0c7578 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ClusterUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ClusterUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -295,13 +295,7 @@ public static boolean scaleClusterWithRestApi(String domainUid, logger.info("Calling curl to scale the cluster"); ExecResult result = Command.withParams(params).executeAndReturnResult(); logger.info("Return values {0}, errors {1}", result.stdout(), result.stderr()); - if (result != null) { - logger.info("Return values {0}, errors {1}", result.stdout(), result.stderr()); - if (result.stdout().contains(expectedMsg) || result.stderr().contains(expectedMsg)) { - return true; - } - } - return false; + return result.stdout().contains(expectedMsg) || result.stderr().contains(expectedMsg); } /** @@ -362,18 +356,17 @@ public static boolean scaleClusterWithRestApiInPod(String domainUid, .append(clusterName) .append("/scale"); - ExecResult result = null; + ExecResult result; try { logger.info("Calling curl to scale the cluster"); result = ExecCommand.exec(command.toString(), true); logger.info("result is: {0}", result.toString()); logger.info("Return values {0}, errors {1}", result.stdout(), result.stderr()); - if (result != null) { - logger.info("Return values {0}, errors {1}", result.stdout(), result.stderr()); - if (result.stdout().contains(expectedMsg) || result.stderr().contains(expectedMsg)) { - return true; - } + + if (result.stdout().contains(expectedMsg) || result.stderr().contains(expectedMsg)) { + return true; } + } catch (IOException | InterruptedException ex) { logger.severe(ex.getMessage()); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index ddbd0a4ae1a..3a846053a2a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -565,11 +565,9 @@ public static DomainResource createDomainResource( String auxiliaryImageVolumeName, String... auxiliaryImageName) { - DomainResource domainCR = CommonMiiTestUtils.createDomainResourceWithAuxiliaryImageAndVolume(domainResourceName, + return createDomainResourceWithAuxiliaryImageAndVolume(domainResourceName, domNamespace, baseImageName, adminSecretName, repoSecretName, encryptionSecretName, auxiliaryImagePath, auxiliaryImageVolumeName, auxiliaryImageName); - - return domainCR; } /** @@ -608,15 +606,7 @@ public static DomainResource createDomainResourceWithAuxiliaryImageAndVolume( domainCR.spec().configuration().model() .withModelHome(auxiliaryImagePath + "/models") .withWdtInstallHome(auxiliaryImagePath + "/weblogic-deploy"); - for (String cmImageName: auxiliaryImageName) { - /* Commented out due to auxiliary image 4.0 changes - domainCR.spec().serverPod() - .addAuxiliaryImagesItem(new AuxiliaryImage() - .image(cmImageName) - .volume(auxiliaryImageVolumeName) - .imagePullPolicy("IfNotPresent")); - */ - } + return domainCR; } @@ -806,7 +796,6 @@ public static DomainResource createDomainResourceWithLogHome( String javaOpt, boolean onlineUpdateEnabled, boolean setDataHome) { - LoggingFacade logger = getLogger(); List securityList = new ArrayList<>(); if (dbSecretName != null) { @@ -1087,7 +1076,7 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN String domainName = adminServerPodName.split("-" + ADMIN_SERVER_NAME_BASE)[0]; String serviceName = ADMIN_SERVER_NAME_BASE; String ingressName = domainNamespace + "-" + domainName + "-" + serviceName + "-" + port; - String hostHeader = domainNamespace + "." + domainName + "." + serviceName;; + String hostHeader = domainNamespace + "." + domainName + "." + serviceName; Optional ingressFound; try { List ingresses = TestActions.listIngresses(domainNamespace); @@ -1112,7 +1101,7 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN ex.printStackTrace(); } } else { - String curlString = null; + String curlString; if (OKE_CLUSTER_PRIVATEIP) { String protocol = "http"; String port = "7001"; @@ -1261,7 +1250,7 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost, String domainName = adminServerPodName.split("-" + ADMIN_SERVER_NAME_BASE)[0]; String serviceName = ADMIN_SERVER_NAME_BASE; String ingressName = domainNamespace + "-" + domainName + "-" + serviceName + "-" + port; - String hostHeader = domainNamespace + "." + domainName + "." + serviceName;; + String hostHeader = domainNamespace + "." + domainName + "." + serviceName; Optional ingressFound; try { List ingresses = TestActions.listIngresses(domainNamespace); @@ -1416,7 +1405,7 @@ public static void createJobToChangePermissionsOnPvHostPath(String pvName, Strin // check job status and fail test if the job failed V1Job job = assertDoesNotThrow(() -> getJob(jobName, namespace), "Getting the job failed"); - if (job != null) { + if (job != null && job.getStatus() != null && job.getStatus().getConditions() != null) { V1JobCondition jobCondition = job.getStatus().getConditions().stream().filter( v1JobCondition -> "Failed".equals(v1JobCondition.getType())) .findAny() @@ -1481,7 +1470,7 @@ public static void createJobToChangePermissionsOnPvHostPath(String pvName, Strin // check job status and fail test if the job failed V1Job job = assertDoesNotThrow(() -> getJob(jobName, namespace), "Getting the job failed"); - if (job != null) { + if (job != null && job.getStatus() != null && job.getStatus().getConditions() != null) { V1JobCondition jobCondition = job.getStatus().getConditions().stream().filter( v1JobCondition -> "Failed".equals(v1JobCondition.getType())) .findAny() diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index 6967989aff1..fa30c49b4a9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -40,13 +40,12 @@ import io.kubernetes.client.openapi.models.V1IngressServiceBackend; import io.kubernetes.client.openapi.models.V1IngressTLS; import io.kubernetes.client.openapi.models.V1ServiceBackendPort; -import oracle.weblogic.domain.ClusterSpec; +import oracle.weblogic.domain.ClusterResource; import oracle.weblogic.domain.DomainCondition; import oracle.weblogic.domain.DomainResource; import oracle.weblogic.kubernetes.TestConstants; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; -import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.logging.LoggingFacade; import org.awaitility.core.ConditionEvaluationListener; import org.awaitility.core.ConditionFactory; @@ -103,6 +102,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.scaleClusterWithRestApiInOpPod; import static oracle.weblogic.kubernetes.actions.TestActions.scaleClusterWithWLDF; import static oracle.weblogic.kubernetes.actions.impl.UniqueName.random; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.getClusterCustomResource; import static oracle.weblogic.kubernetes.assertions.TestAssertions.credentialsNotValid; import static oracle.weblogic.kubernetes.assertions.TestAssertions.credentialsValid; import static oracle.weblogic.kubernetes.assertions.TestAssertions.isPodReady; @@ -166,11 +166,8 @@ public static ConditionFactory createStandardRetryPolicyWithAtMost(long minutes) } public static ConditionFactory withStandardRetryPolicy = createStandardRetryPolicyWithAtMost(5); - public static ConditionFactory withStandardRetryPolicyIgnoringExceptions = - createStandardRetryPolicyWithAtMost(5).ignoreExceptions(); public static ConditionFactory withLongRetryPolicy = createStandardRetryPolicyWithAtMost(15); - private static final String TMP_FILE_NAME = "temp-download-file.out"; private static int adminListenPort = 7001; /** @@ -241,7 +238,7 @@ public static boolean testUntilNoException(ConditionFactory conditionFactory, Ca private static ConditionEvaluationListener createConditionEvaluationListener( LoggingFacade logger, String msg, Object... params) { - return new ConditionEvaluationListener() { + return new ConditionEvaluationListener<>() { @Override public void conditionEvaluated(EvaluatedCondition condition) { int paramsSize = params != null ? params.length : 0; @@ -389,9 +386,10 @@ public static void checkServiceDoesNotExist(String serviceName, String namespace * @return true, if the cluster replica count is matched */ public static boolean checkClusterReplicaCountMatches(String clusterName, - String namespace, Integer replicaCount) throws ApiException { - ClusterSpec clusterSpec = Kubernetes.getClusterCustomResource(clusterName, namespace, CLUSTER_VERSION).getSpec(); - return Optional.ofNullable(clusterSpec).get().replicas() == replicaCount; + String namespace, int replicaCount) throws ApiException { + ClusterResource clusterResource = getClusterCustomResource(clusterName, namespace, CLUSTER_VERSION); + return clusterResource != null + && clusterResource.getSpec().replicas() == replicaCount; } /** Scale the WebLogic cluster to specified number of servers. @@ -485,7 +483,7 @@ public static void scaleAndVerifyCluster(String clusterName, clusterName, domainUid, domainNamespace, replicasAfterScale); if (withRestApi) { if (OKE_CLUSTER && args != null && args.length > 0) { - String operatorPodName = (args == null || args.length == 0) ? null : args[0]; + String operatorPodName = args[0]; int opExtPort = 8081; assertThat(assertDoesNotThrow(() -> scaleClusterWithRestApiInOpPod(domainUid, clusterName, replicasAfterScale, operatorPodName, opExtPort, opNamespace, opServiceAccount))) @@ -1106,15 +1104,15 @@ public static Callable runClientInsidePodVerifyResult(String podName, * Adds proxy extra arguments for image builder command. **/ public static String getImageBuilderExtraArgs() { - StringBuffer extraArgs = new StringBuffer(""); + StringBuffer extraArgs = new StringBuffer(); String httpsproxy = HTTPS_PROXY; String httpproxy = HTTP_PROXY; String noproxy = NO_PROXY; LoggingFacade logger = getLogger(); logger.info(" httpsproxy : " + httpsproxy); - String proxyHost = ""; - StringBuffer mvnArgs = new StringBuffer(""); + String proxyHost; + StringBuffer mvnArgs = new StringBuffer(); if (httpsproxy != null) { logger.info(" httpsproxy : " + httpsproxy); proxyHost = httpsproxy.substring(httpsproxy.lastIndexOf("www"), httpsproxy.lastIndexOf(":")); @@ -1135,7 +1133,7 @@ public static String getImageBuilderExtraArgs() { logger.info(" noproxy : " + noproxy); extraArgs.append(String.format(" --build-arg no_proxy=%s",noproxy)); } - if (!mvnArgs.equals("")) { + if (mvnArgs.length() > 0) { extraArgs.append(" --build-arg MAVEN_OPTS=\" " + mvnArgs.toString() + "\""); } return extraArgs.toString(); @@ -1166,7 +1164,11 @@ public static void verifyServerCommunication(String curlRequest, List ma } catch (IOException | InterruptedException ex) { logger.severe(ex.getMessage()); } - String response = result.stdout().trim(); + + String response = ""; + if (result.stdout() != null) { + response = result.stdout().trim(); + } logger.info(response); for (var managedServer : managedServers.entrySet()) { boolean connectToOthers = true; @@ -1832,7 +1834,7 @@ public static boolean checkImageContainerReady(String containerName) { ex.printStackTrace(); } - return result.stdout().contains("The server started in RUNNING mode"); + return result != null && result.stdout() != null && result.stdout().contains("The server started in RUNNING mode"); } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java index bd36722422d..e4f7dae700c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java @@ -1,7 +1,6 @@ -// Copyright (c) 2021, 2022, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - package oracle.weblogic.kubernetes.utils; import java.io.IOException; @@ -162,17 +161,11 @@ public static void createConfigMapForDomainCreation(String configMapName, List

    configMapExist(String nameSpace, String configMapName) throws ApiException { List cmList = Kubernetes.listConfigMaps(nameSpace).getItems(); V1ConfigMap configMapToModify = cmList.stream() - .filter(cm -> configMapName.equals(cm.getMetadata().getName())) + .filter(cm -> cm.getMetadata() != null && configMapName.equals(cm.getMetadata().getName())) .findAny() .orElse(null); - return () -> { - if (configMapToModify != null) { - return true; - } else { - return false; - } - }; + return () -> configMapToModify != null; } /** @@ -183,17 +176,11 @@ public static Callable configMapExist(String nameSpace, String configMa public static Callable configMapDoesNotExist(String nameSpace, String configMapName) throws ApiException { List cmList = Kubernetes.listConfigMaps(nameSpace).getItems(); V1ConfigMap configMapToModify = cmList.stream() - .filter(cm -> configMapName.equals(cm.getMetadata().getName())) + .filter(cm -> cm.getMetadata() != null && configMapName.equals(cm.getMetadata().getName())) .findAny() .orElse(null); - return () -> { - if (configMapToModify == null) { - return true; - } else { - return false; - } - }; + return () -> configMapToModify == null; } /** @@ -241,14 +228,16 @@ public static void editConfigMap(String oldRegex, String newRegex, String configFileName) throws ApiException { List cmList = Kubernetes.listConfigMaps(nameSpace).getItems(); V1ConfigMap configMapToModify = cmList.stream() - .filter(cm -> cmName.equals(cm.getMetadata().getName())) + .filter(cm -> cm.getMetadata() != null && cmName.equals(cm.getMetadata().getName())) .findAny() .orElse(null); assertNotNull(configMapToModify,"Can't find cm for " + cmName); Map cmData = configMapToModify.getData(); - - String values = cmData.get("logstash.conf").replace(oldRegex,newRegex); + String values = null; + if (cmData != null) { + values = cmData.get("logstash.conf").replace(oldRegex, newRegex); + } assertNotNull(values, "can't find values for key prometheus.yml"); cmData.replace(configFileName, values); @@ -258,7 +247,7 @@ public static void editConfigMap(String oldRegex, String newRegex, cmList = Kubernetes.listConfigMaps(nameSpace).getItems(); configMapToModify = cmList.stream() - .filter(cm -> cmName.equals(cm.getMetadata().getName())) + .filter(cm -> cm.getMetadata() != null && cmName.equals(cm.getMetadata().getName())) .findAny() .orElse(null); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java index 3a8da15def8..2337e61a6a5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java @@ -134,7 +134,6 @@ public class DbUtils { private static V1Service oracleDBService = null; private static V1Deployment oracleDbDepl = null; - private static int suffixCount = 0; private static Map dbMap = new HashMap<>(); /** @@ -146,7 +145,7 @@ public class DbUtils { * @param dbNamespace namespace where DB and RCU schema are going to start * @param dbPort NodePort of DB * @param dbUrl URL of DB - * @throws Exception if any error occurs when setting up RCU database + * @throws ApiException if any error occurs when setting up RCU database */ public static synchronized void setupDBandRCUschema(String dbImage, String fmwImage, String rcuSchemaPrefix, @@ -167,7 +166,6 @@ public static synchronized void setupDBandRCUschema(String dbImage, String fmwIm logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " + " dbNamespace: {3}:", fmwImage, rcuSchemaPrefix, dbUrl, dbNamespace); createRcuSchema(fmwImage, rcuSchemaPrefix, dbUrl, dbNamespace); - } /** @@ -448,15 +446,15 @@ public static boolean isPodReady(String namespace, String labelSelector, String LoggingFacade logger = getLogger(); boolean status = false; V1Pod pod = getPod(namespace, labelSelector, podName); - if (pod != null) { + if (pod != null && pod.getStatus() != null && pod.getStatus().getConditions() != null) { // get the podCondition with the 'Ready' type field V1PodCondition v1PodReadyCondition = pod.getStatus().getConditions().stream() .filter(v1PodCondition -> "Ready".equals(v1PodCondition.getType())) .findAny() .orElse(null); - if (v1PodReadyCondition != null) { + if (v1PodReadyCondition != null && v1PodReadyCondition.getStatus() != null) { status = v1PodReadyCondition.getStatus().equalsIgnoreCase("true"); if (status) { logger.info("Pod {0} is READY in namespace {1}", podName, namespace); @@ -536,11 +534,11 @@ private static boolean createRcuRepository(String dbNamespace, String dbUrl, */ public static String getPodNameOfDb(String dbNamespace, String podPrefix) throws ApiException { String podName = null; - V1PodList pods = null; - pods = Kubernetes.listPods(dbNamespace, null); + V1PodList pods = Kubernetes.listPods(dbNamespace, null); if (pods.getItems().size() != 0) { for (V1Pod pod : pods.getItems()) { - if (pod != null && pod.getMetadata().getName().startsWith(podPrefix)) { + if (pod != null && pod.getMetadata() != null && pod.getMetadata().getName() != null + && pod.getMetadata().getName().startsWith(podPrefix)) { podName = pod.getMetadata().getName(); break; } @@ -604,7 +602,9 @@ public static Integer getDBNodePort(String dbNamespace, String dbName) { logger.info(dump(Kubernetes.listServices(dbNamespace))); List services = listServices(dbNamespace).getItems(); for (V1Service service : services) { - if (service.getMetadata().getName().startsWith(dbName)) { + if (service.getMetadata() != null && service.getMetadata().getName() != null + && service.getMetadata().getName().startsWith(dbName) && service.getSpec() != null + && service.getSpec().getPorts() != null) { return service.getSpec().getPorts().get(0).getNodePort(); } } @@ -1051,19 +1051,23 @@ public static void createPV(String pvName) { .putCapacityItem("storage", Quantity.fromString("100Gi")) .persistentVolumeReclaimPolicy("Recycle") .accessModes(Arrays.asList("ReadWriteMany"))); - if (OKD) { - v1pv.getSpec() - .storageClassName("okd-nfsmnt") - .nfs(new V1NFSVolumeSource() - .path(PV_ROOT) - .server(NFS_SERVER) - .readOnly(false)); - } else { - v1pv.getSpec() - .storageClassName("weblogic-domain-storage-class") - .hostPath(new V1HostPathVolumeSource() - .path(pvHostPath.toString())); + + if (v1pv != null && v1pv.getSpec() != null) { + if (OKD) { + v1pv.getSpec() + .storageClassName("okd-nfsmnt") + .nfs(new V1NFSVolumeSource() + .path(PV_ROOT) + .server(NFS_SERVER) + .readOnly(false)); + } else { + v1pv.getSpec() + .storageClassName("weblogic-domain-storage-class") + .hostPath(new V1HostPathVolumeSource() + .path(pvHostPath.toString())); + } } + logger.info(Yaml.dump(v1pv)); boolean success = assertDoesNotThrow(() -> createPersistentVolume(v1pv), "Failed to create persistent volume"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java index 60493b010fd..f8c3ddac0e9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java @@ -201,7 +201,7 @@ private static void createDeployJob(String deployScriptConfigMap, String namespa // check job status and fail test if the job failed to deploy V1Job job = getJob(jobName, namespace); - if (job != null) { + if (job != null && job.getStatus() != null && job.getStatus().getConditions() != null) { V1JobCondition jobCondition = job.getStatus().getConditions().stream().filter( v1JobCondition -> "Failed".equals(v1JobCondition.getType())) .findAny() @@ -210,12 +210,14 @@ private static void createDeployJob(String deployScriptConfigMap, String namespa logger.severe("Job {0} failed to do deployment", jobName); List pods = listPods(namespace, "job-name=" + jobName).getItems(); if (!pods.isEmpty()) { - logger.severe(getPodLog(pods.get(0).getMetadata().getName(), namespace)); + V1Pod pod = pods.get(0); + if (pod != null && pod.getMetadata() != null) { + logger.severe(getPodLog(pod.getMetadata().getName(), namespace)); + } fail("Deployment job failed"); } } } - } /** @@ -306,14 +308,14 @@ public static ExecResult deployUsingRest(String hostAndPort, String userName, String password, String targets, Path archivePath, String hostHeader, String appName) { final LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer headerString = null; + ExecResult result; + StringBuffer headerString; if (hostHeader != null) { headerString = new StringBuffer("-H 'host: "); headerString.append(hostHeader) .append(" ' "); } else { - headerString = new StringBuffer(""); + headerString = new StringBuffer(); } StringBuffer curlString = new StringBuffer("status=$(curl -g --noproxy '*' "); curlString.append(" --user " + userName + ":" + password); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java index 2606ac78422..16975d25cdd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java @@ -467,7 +467,7 @@ public static void removeClusterInDomainResource(String clusterName, String doma logger.info("Removing the cluster {0} from domain resource {1}", clusterName, domainUid); DomainResource domainCustomResource = getDomainCustomResource(domainUid, namespace); Optional cluster = domainCustomResource.getSpec() - .getClusters().stream().filter(o -> o.getName().equals(clusterName)).findAny(); + .getClusters().stream().filter(o -> o.getName() != null && o.getName().equals(clusterName)).findAny(); int clusterIndex = -1; if (cluster.isPresent()) { clusterIndex = domainCustomResource.getSpec().getClusters().indexOf(cluster.get()); @@ -482,8 +482,8 @@ public static void removeClusterInDomainResource(String clusterName, String doma V1Patch patch = new V1Patch(patchStr); assertTrue(patchDomainCustomResource(domainUid, namespace, patch, V1Patch.PATCH_FORMAT_JSON_PATCH), "Failed to patch domain"); - Callable clusterNotFound = () -> !getDomainCustomResource(domainUid, namespace).getSpec() - .getClusters().stream().anyMatch(c -> c.getName().equals(clusterName)); + Callable clusterNotFound = () -> getDomainCustomResource(domainUid, namespace).getSpec() + .getClusters().stream().noneMatch(c -> c.getName() != null && c.getName().equals(clusterName)); testUntil(clusterNotFound, logger, "cluster {0} to be removed from domain resource in namespace {1}", clusterName, namespace); } else { @@ -533,7 +533,7 @@ public static void patchDomainWithAuxiliaryImageAndVerify(String oldImageName, S assertFalse(auxiliaryImageList.isEmpty(), "AuxiliaryImage list is empty"); String searchString; - int index = 0; + int index; AuxiliaryImage ai = auxiliaryImageList.stream() .filter(auxiliaryImage -> oldImageName.equals(auxiliaryImage.getImage())) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java index 3f0d8dece65..8296edfc63c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -29,7 +29,6 @@ import oracle.weblogic.kubernetes.actions.impl.primitive.Installer; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.logging.LoggingFacade; -import oracle.weblogic.kubernetes.utils.ExecResult; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; @@ -90,10 +89,7 @@ public static void checkFile(String fileName) throws FileNotFoundException { */ public static boolean doesFileExist(String fileName) { File file = new File(fileName); - if (file.exists() && file.isFile()) { - return true; - } - return false; + return file.exists() && file.isFile(); } /** @@ -104,10 +100,7 @@ public static boolean doesFileExist(String fileName) { */ public static boolean doesDirExist(String dirName) { File dir = new File(dirName); - if (dir.exists() && dir.isDirectory()) { - return true; - } - return false; + return dir.exists() && dir.isDirectory(); } /** @@ -125,7 +118,6 @@ public static void cleanupDirectory(String dir) throws IOException { } cleanDirectory(file); - } /** @@ -355,7 +347,7 @@ public static void copy(Path source, Path dest) throws IOException { */ public static ExecResult copyFileToImageContainer(String containerName, String source, String dest) { LoggingFacade logger = getLogger(); - ExecResult result = null; + ExecResult result; // create a WebLogic container String cpToContainerCmd = new StringBuffer(WLSIMG_BUILDER + " cp ") @@ -425,7 +417,7 @@ public static boolean checkFileCopiedToImageContainer(String containerName, Stri logger.info("checkCmd: caught unexpected exception {0}", ex.getMessage()); } - return result.stdout().contains(dest); + return result != null && result.stdout() != null && result.stdout().contains(dest); } /** @@ -436,7 +428,6 @@ public static boolean checkFileCopiedToImageContainer(String containerName, Stri */ public static void readFileCopiedInImageContainer(String containerName, String dest) { LoggingFacade logger = getLogger(); - ExecResult result = null; // check the file is copied over successfully //String readCmd = new StringBuffer(WLSIMG_BUILDER + " exec -it " @@ -448,7 +439,7 @@ public static void readFileCopiedInImageContainer(String containerName, String d logger.info("Command to cat file {0}: ", dest, readCmd); try { - result = exec(readCmd, true); + exec(readCmd, true); } catch (Exception ex) { logger.info("checkCmd: caught unexpected exception {0}", ex.getMessage()); } @@ -464,7 +455,7 @@ public static String createZipFile(Path dirPath) { String zipFileName = dirPath.toString().concat(".zip"); try { final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFileName)); - Files.walkFileTree(dirPath, new SimpleFileVisitor() { + Files.walkFileTree(dirPath, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) { try { @@ -528,11 +519,7 @@ public static boolean doesFileExistInPod(String namespace, String podName, Strin ExecResult result = execCommand(namespace, podName, null, true, "/bin/sh", "-c", "find " + filename); - if (result.stdout().contains(filename)) { - return true; - } else { - return false; - } + return result.stdout().contains(filename); } /** @@ -606,10 +593,7 @@ public static Path generateFileFromTemplate( */ public static Callable isFileExistAndNotEmpty(String fileName) { File file = new File(fileName); - return () -> { - boolean fileReady = (file.exists() && file.length() != 0); - return fileReady; - }; + return () -> file.exists() && file.length() != 0; } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java index 60c625975b4..55ece386c9d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java @@ -189,7 +189,7 @@ public static DomainResource createIstioDomainResource( String opssWalletPasswordSecretName, int replicaCount, String miiImage, String configmapName) { // create the domain CR - DomainResource domain = new DomainResource() + return new DomainResource() .apiVersion(DOMAIN_API_VERSION) .kind("Domain") .metadata(new V1ObjectMeta() @@ -225,8 +225,6 @@ public static DomainResource createIstioDomainResource( .runtimeEncryptionSecret(encryptionSecretName)) .addSecretsItem(rcuAccessSecretName) .introspectorJobActiveDeadlineSeconds(600L))); - - return domain; } /** @@ -253,7 +251,7 @@ public static DomainResource createDomainResourceOnPv(String domainUid, int t3ChannelPort) { // create a domain custom resource configuration object - DomainResource domain = new DomainResource() + return new DomainResource() .apiVersion(DOMAIN_API_VERSION) .kind("Domain") .metadata(new V1ObjectMeta() @@ -299,8 +297,6 @@ public static DomainResource createDomainResourceOnPv(String domainUid, .addChannelsItem(new Channel() .channelName("T3Channel") .nodePort(t3ChannelPort))))); - - return domain; } /** @@ -672,7 +668,7 @@ public static Configuration getConfiguration(String pvName, String pvcName, Map pvCapacity, Map pvcRequest, String storageClassName, String testClass) { Configuration configuration = new Configuration(); - PersistentVolume pv = null; + PersistentVolume pv; if (OKE_CLUSTER) { storageClassName = "oci-fss"; } else if (OKD) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java index eb10dc0faa4..0b561b37194 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -368,7 +368,7 @@ public static String createImageAndVerify(String imageNameBase, if (buildAppDirList.size() != 0 && buildAppDirList.get(0) != null) { // build an application archive using what is in resources/apps/APP_NAME - String zipFile = ""; + String zipFile; if (oneArchiveContainsMultiApps) { for (String buildAppDirs : buildAppDirList) { assertTrue(buildAppArchive(appParams @@ -549,7 +549,8 @@ public static void createImageRegistrySecret(String userName, String password, V1SecretList listSecrets = listSecrets(namespace); if (listSecrets != null) { for (V1Secret item : listSecrets.getItems()) { - if (item.getMetadata().getName().equals(secretName)) { + if (item.getMetadata() != null && item.getMetadata().getName() != null + && item.getMetadata().getName().equals(secretName)) { logger.info("Secret {0} already exists in namespace {1}, skipping secret creation", secretName, namespace); return; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index 35268019473..759c2dc62f1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -140,8 +140,8 @@ public static void uninstallIstio() { */ public static int getIstioHttpIngressPort() { LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer getIngressPort = null; + ExecResult result; + StringBuffer getIngressPort; getIngressPort = new StringBuffer(KUBERNETES_CLI + " -n istio-system get service istio-ingressgateway "); getIngressPort.append("-o jsonpath='{.spec.ports[?(@.name==\"http2\")].nodePort}'"); logger.info("getIngressPort: " + KUBERNETES_CLI + " command {0}", new String(getIngressPort)); @@ -166,8 +166,8 @@ public static int getIstioHttpIngressPort() { */ public static int getIstioSecureIngressPort() { LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer getSecureIngressPort = null; + ExecResult result; + StringBuffer getSecureIngressPort; getSecureIngressPort = new StringBuffer(KUBERNETES_CLI + " -n istio-system get service istio-ingressgateway "); getSecureIngressPort.append("-o jsonpath='{.spec.ports[?(@.name==\"https\")].nodePort}'"); logger.info("getSecureIngressPort: " + KUBERNETES_CLI + " command {0}", new String(getSecureIngressPort)); @@ -192,8 +192,8 @@ public static int getIstioSecureIngressPort() { */ public static int getIstioTcpIngressPort() { LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer getTcpIngressPort = null; + ExecResult result; + StringBuffer getTcpIngressPort; getTcpIngressPort = new StringBuffer(KUBERNETES_CLI + " -n istio-system get service istio-ingressgateway "); getTcpIngressPort.append("-o jsonpath='{.spec.ports[?(@.name==\"tcp\")].nodePort}'"); logger.info("getTcpIngressPort: " + KUBERNETES_CLI + " command {0}", new String(getTcpIngressPort)); @@ -219,8 +219,8 @@ public static int getIstioTcpIngressPort() { */ public static boolean deployHttpIstioGatewayAndVirtualservice(Path configPath) { LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer deployIstioGateway = null; + ExecResult result; + StringBuffer deployIstioGateway; deployIstioGateway = new StringBuffer(KUBERNETES_CLI + " apply -f "); deployIstioGateway.append(configPath); logger.info("deployIstioGateway: " + KUBERNETES_CLI + " command {0}", new String(deployIstioGateway)); @@ -243,8 +243,8 @@ public static boolean deployHttpIstioGatewayAndVirtualservice(Path configPath) { public static boolean deployTcpIstioGatewayAndVirtualservice( Path configPath) { LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer deployIstioGateway = null; + ExecResult result; + StringBuffer deployIstioGateway; deployIstioGateway = new StringBuffer(KUBERNETES_CLI + " apply -f "); deployIstioGateway.append(configPath); logger.info("deployIstioGateway: " + KUBERNETES_CLI + " command {0}", new String(deployIstioGateway)); @@ -267,8 +267,8 @@ public static boolean deployTcpIstioGatewayAndVirtualservice( public static boolean deployIstioDestinationRule( Path configPath) { LoggingFacade logger = getLogger(); - ExecResult result = null; - StringBuffer deployIstioGateway = null; + ExecResult result; + StringBuffer deployIstioGateway; deployIstioGateway = new StringBuffer(KUBERNETES_CLI + " apply -f "); deployIstioGateway.append(configPath); logger.info("deployIstioDestinationRule: " + KUBERNETES_CLI + " command {0}", new String(deployIstioGateway)); @@ -323,8 +323,8 @@ public static boolean deployIstioPrometheus( assertDoesNotThrow(() -> replaceStringInFile(targetPromFile.toString(), "prometheus_tag", PROMETHEUS_IMAGE_TAG)); - ExecResult result = null; - StringBuffer deployIstioPrometheus = null; + ExecResult result; + StringBuffer deployIstioPrometheus; deployIstioPrometheus = new StringBuffer(KUBERNETES_CLI + " apply -f "); deployIstioPrometheus.append(targetPromFile.toString()); logger.info("deployIstioPrometheus: " + KUBERNETES_CLI + " command {0}", new String(deployIstioPrometheus)); @@ -337,13 +337,12 @@ public static boolean deployIstioPrometheus( logger.info("deployIstioPrometheus: " + KUBERNETES_CLI + " returned {0}", result.toString()); try { for (var item : listPods("istio-system", null).getItems()) { - if (item.getMetadata() != null) { - if (item.getMetadata().getName().contains("prometheus")) { - logger.info("Waiting for pod {0} to be ready in namespace {1}", - item.getMetadata().getName(), "istio-system"); - checkPodReady(item.getMetadata().getName(), null, "istio-system"); - checkServiceExists("prometheus", "istio-system"); - } + if (item.getMetadata() != null && item.getMetadata().getName() != null + && item.getMetadata().getName().contains("prometheus")) { + logger.info("Waiting for pod {0} to be ready in namespace {1}", + item.getMetadata().getName(), "istio-system"); + checkPodReady(item.getMetadata().getName(), null, "istio-system"); + checkServiceExists("prometheus", "istio-system"); } } } catch (ApiException e) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java index 4376eafde06..c5f28e78747 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2022, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -165,7 +165,7 @@ public static void createDomainJob(String image, String pvName, String pvcName, // check job status and fail test if the job failed to create domain V1Job job = assertDoesNotThrow(() -> getJob(jobName, namespace), "Getting the job failed"); - if (job != null) { + if (job != null && job.getStatus() != null && job.getStatus().getConditions() != null) { V1JobCondition jobCondition = job.getStatus().getConditions().stream().filter( v1JobCondition -> "Failed".equals(v1JobCondition.getType())) .findAny() @@ -175,7 +175,7 @@ public static void createDomainJob(String image, String pvName, String pvcName, List pods = assertDoesNotThrow(() -> listPods( namespace, "job-name=" + jobName).getItems(), "Listing pods failed"); - if (!pods.isEmpty()) { + if (!pods.isEmpty() && pods.get(0).getMetadata() != null) { String podLog = assertDoesNotThrow(() -> getPodLog(pods.get(0).getMetadata().getName(), namespace), "Failed to get pod log"); logger.severe(podLog); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java index fbfbad24d3a..09b2e194dfb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/K8sEvents.java @@ -337,7 +337,7 @@ public static int getDomainEventCount( try { List events = Kubernetes.listOpGeneratedNamespacedEvents(domainNamespace); for (CoreV1Event event : events) { - if (event.getMetadata() != null) { + if (event != null && event.getMetadata() != null) { Map labels = event.getMetadata().getLabels(); if (event.getReason() != null && event.getReason().equals(reason) && event.getType() != null && event.getType().equals(type) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index afd6b68cf51..d595e10b5c5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -651,7 +651,6 @@ public static boolean createTraefikIngressRoutingRules(String domainNamespace, * @throws Exception fails if not generated after MaxIterations number is reached. */ public static String getLbExternalIp(String lbrelname, String lbns) throws Exception { - int i = 0; LoggingFacade logger = getLogger(); String cmdip = KUBERNETES_CLI + " get svc --namespace " + lbns diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoggingUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoggingUtil.java index b6014fd1785..fe8dc4cd808 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoggingUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoggingUtil.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -25,7 +25,9 @@ import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1PersistentVolume; +import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimList; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; +import io.kubernetes.client.openapi.models.V1PersistentVolumeList; import io.kubernetes.client.openapi.models.V1Pod; import io.kubernetes.client.openapi.models.V1PodSpec; import io.kubernetes.client.openapi.models.V1Volume; @@ -40,6 +42,8 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.VZ_ENV; import static oracle.weblogic.kubernetes.actions.TestActions.getPodLog; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listPersistentVolumeClaims; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listPersistentVolumes; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podDoesNotExist; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podReady; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; @@ -112,7 +116,7 @@ public static void collectLogs(String namespace, String resultDir) { // get namespaces try { for (var ns : Kubernetes.listNamespacesAsObjects().getItems()) { - if (namespace.equals(ns.getMetadata().getName())) { + if (ns.getMetadata() != null && namespace.equals(ns.getMetadata().getName())) { writeToFile(ns, resultDir, namespace + ".list.namespace.log"); } } @@ -122,7 +126,7 @@ public static void collectLogs(String namespace, String resultDir) { // get pvc try { - writeToFile(Kubernetes.listPersistentVolumeClaims(namespace), resultDir, + writeToFile(listPersistentVolumeClaims(namespace), resultDir, namespace + ".list.persistent-volume-claims.log"); } catch (Exception ex) { logger.warning(ex.getMessage()); @@ -130,13 +134,17 @@ public static void collectLogs(String namespace, String resultDir) { // archive persistent volume contents List pvList = new ArrayList<>(); - for (var pv : Kubernetes.listPersistentVolumes().getItems()) { - for (var pvc : Kubernetes.listPersistentVolumeClaims(namespace).getItems()) { - if (pv.getSpec().getStorageClassName() - .equals(pvc.getSpec().getStorageClassName()) - && pv.getMetadata().getName() - .equals(pvc.getSpec().getVolumeName())) { - pvList.add(pv); + V1PersistentVolumeList persistentVolumeList = listPersistentVolumes(); + V1PersistentVolumeClaimList persistentVolumeClaimList = listPersistentVolumeClaims(namespace); + if (persistentVolumeList != null && persistentVolumeClaimList != null) { + for (var pv : persistentVolumeList.getItems()) { + for (var pvc : persistentVolumeClaimList.getItems()) { + if (pv.getSpec() != null && pvc.getSpec() != null && pv.getSpec().getStorageClassName() != null + && pv.getSpec().getStorageClassName().equals(pvc.getSpec().getStorageClassName()) + && pv.getMetadata() != null && pv.getMetadata().getName() != null + && pv.getMetadata().getName().equals(pvc.getSpec().getVolumeName())) { + pvList.add(pv); + } } } } @@ -551,32 +559,4 @@ public static void checkPodLogContainsString(String namespace, String podName, podName, namespace); } - - private void archivePV(String namespace, String resultDir) { - // archive persistent volume contents - List pvList = new ArrayList<>(); - for (var pv : Kubernetes.listPersistentVolumes().getItems()) { - for (var pvc : Kubernetes.listPersistentVolumeClaims(namespace).getItems()) { - if (pv.getSpec().getStorageClassName() - .equals(pvc.getSpec().getStorageClassName()) - && pv.getMetadata().getName() - .equals(pvc.getSpec().getVolumeName())) { - pvList.add(pv); - String pvName = pv.getMetadata().getName(); - String pvcName = pvc.getMetadata().getName(); - try { - if (pv.getMetadata().getDeletionTimestamp() == null) { - copyFromPV(namespace, pvcName, pvName, - Files.createDirectories( - Paths.get(resultDir, pvcName, pvName))); - } - } catch (ApiException ex) { - getLogger().warning(ex.getResponseBody()); - } catch (IOException ex) { - getLogger().warning(ex.getMessage()); - } - } - } - } - } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index 223d478c938..aa58be3bdb8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -339,7 +339,7 @@ public static void editPrometheusCM(String oldRegex, String newRegex, String prometheusNS, String cmName) throws ApiException { List cmList = Kubernetes.listConfigMaps(prometheusNS).getItems(); V1ConfigMap promCm = cmList.stream() - .filter(cm -> cmName.equals(cm.getMetadata().getName())) + .filter(cm -> cm.getMetadata() != null && cmName.equals(cm.getMetadata().getName())) .findAny() .orElse(null); @@ -683,7 +683,8 @@ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, V1SecretList listSecrets = listSecrets(grafanaNamespace); if (null != listSecrets) { for (V1Secret item : listSecrets.getItems()) { - if (item.getMetadata().getName().equals("grafana-secret")) { + if (item.getMetadata() != null && item.getMetadata().getName() != null + && item.getMetadata().getName().equals("grafana-secret")) { secretExists = true; break; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MySQLDBUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MySQLDBUtils.java index 2cdfbd14048..0a91231ec73 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MySQLDBUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MySQLDBUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -33,6 +33,7 @@ import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -92,7 +93,11 @@ private static void startMySQLDB(String name, String secretName, String namespac .name("mysql") .containerPort(3306)))))); V1Pod pod = assertDoesNotThrow(() -> Kubernetes.createPod(namespace, mysqlPod)); - checkPodReady(pod.getMetadata().getName(), null, namespace); + if (pod != null && pod.getMetadata() != null) { + checkPodReady(pod.getMetadata().getName(), null, namespace); + } else { + getLogger().info("pod is null or pod metadata is null"); + } } //create services for MySQL database diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PersistentVolumeUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PersistentVolumeUtils.java index 3a4ba64056a..e1c0df7d981 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PersistentVolumeUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PersistentVolumeUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. @@ -78,6 +78,8 @@ public static void createPVPVCAndVerify(V1PersistentVolume v1pv, assertNotNull(v1pv, "v1pv is null"); assertNotNull(v1pvc, "v1pvc is null"); + assertNotNull(v1pv.getMetadata(), "v1pv metadata is null"); + assertNotNull(v1pvc.getMetadata(), "v1pvc metadata is null"); String pvName = v1pv.getMetadata().getName(); String pvcName = v1pvc.getMetadata().getName(); @@ -124,6 +126,11 @@ public static void createPVPVCAndVerify(V1PersistentVolume v1pv, String labelSelector, String namespace, String storageClassName, Path pvHostPath) { LoggingFacade logger = getLogger(); + assertNotNull(v1pv, "v1pv is null"); + assertNotNull(v1pvc, "v1pvc is null"); + assertNotNull(v1pv.getSpec(), "v1pv spec is null"); + assertNotNull(v1pvc.getSpec(), "v1pvc spec is null"); + if (!OKE_CLUSTER && !OKD) { logger.info("Creating PV directory {0}", pvHostPath); assertDoesNotThrow(() -> deleteDirectory(pvHostPath.toFile()), "deleteDirectory failed with IOException"); @@ -212,6 +219,9 @@ public static void setVolumeSource(Path pvHostPath, V1PersistentVolume v1pv) { } private static void setVolumeSource(Path pvHostPath, V1PersistentVolume v1pv, String storageClassName) { + assertNotNull(v1pv, "v1pv is null"); + assertNotNull(v1pv.getSpec(), "v1pv spec is null"); + if (OKE_CLUSTER) { String fssDir = FSS_DIR[new Random().nextInt(FSS_DIR.length)]; LoggingFacade logger = getLogger(); @@ -298,6 +308,9 @@ public static void createPVC(String pvName, String pvcName, String domainUid, .putLabelsItem("weblogic.resourceVersion", "domain-v2") .putLabelsItem("weblogic.domainUid", domainUid)); + assertNotNull(v1pvc, "v1pvc is null"); + assertNotNull(v1pvc.getSpec(), "v1pvc spec is null"); + if (OKE_CLUSTER) { v1pvc.getSpec() .storageClassName("oci-fss"); @@ -380,7 +393,7 @@ public static void createPvAndPvc(String nameSuffix, String namespace, HashMap labels, String className) throws IOException { LoggingFacade logger = getLogger(); - V1PersistentVolume v1pv = null; + V1PersistentVolume v1pv; logger.info("creating persistent volume and persistent volume claim"); // create persistent volume and persistent volume claims // when tests are running in local box the PV directories need to exist @@ -405,7 +418,7 @@ public static void createPvAndPvc(String nameSuffix, String namespace, boolean hasLabels = false; String labelSelector = null; - if (labels != null || !labels.isEmpty()) { + if (labels != null && !labels.isEmpty() && v1pv.getMetadata() != null) { hasLabels = true; v1pv.getMetadata().setLabels(labels); labelSelector = labels.entrySet() @@ -424,9 +437,11 @@ public static void createPvAndPvc(String nameSuffix, String namespace, .metadata(new V1ObjectMeta() .name("pvc-" + nameSuffix) .namespace(namespace)); - if (hasLabels) { + if (hasLabels && v1pvc.getMetadata() != null) { v1pvc.getMetadata().setLabels(labels); } + + assertNotNull(v1pvc.getSpec(), "v1pvc spec is null"); if (OKE_CLUSTER) { v1pvc.getSpec() .storageClassName("oci-fss"); @@ -500,6 +515,7 @@ public static synchronized void execCommandInPv(String domainNamespace, String p assertFalse(result.exitValue() != 0 && result.stderr() != null && !result.stderr().isEmpty(), String.format("Command %s failed with exit value %s, stderr %s, stdout %s", commandToExecuteInsidePod, result.exitValue(), result.stderr(), result.stdout())); + assertNotNull(serverPod.getMetadata(), "serverpod metadata is null"); assertDoesNotThrow(() -> deletePod(serverPod.getMetadata().getName(), domainNamespace)); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PodUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PodUtils.java index 6c1d04d1de7..339b79ab822 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PodUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/PodUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -61,7 +61,7 @@ public class PodUtils { */ public static void execInPod(V1Pod pod, String containerName, boolean redirectToStdout, String command) { LoggingFacade logger = getLogger(); - ExecResult exec = null; + ExecResult exec; try { logger.info("Executing command {0}", command); exec = Exec.exec(pod, containerName, redirectToStdout, "/bin/sh", "-c", command); @@ -551,11 +551,12 @@ public static boolean checkInUncompletedIntroPodLogContainsRegex(String regex, S */ public static String getPodName(String namespace, String podPrefix) throws ApiException { String podName = null; - V1PodList pods = null; + V1PodList pods; pods = Kubernetes.listPods(namespace, null); if (pods.getItems().size() != 0) { for (V1Pod pod : pods.getItems()) { - if (pod != null && pod.getMetadata().getName().startsWith(podPrefix)) { + if (pod != null && pod.getMetadata() != null && pod.getMetadata().getName() != null + && pod.getMetadata().getName().startsWith(podPrefix)) { podName = pod.getMetadata().getName(); break; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/SecretUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/SecretUtils.java index 1c52b40f365..3263b43ab63 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/SecretUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/SecretUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -274,7 +274,7 @@ public static String getServiceAccountToken(String serviceAccount, String namesp logger.info("Getting service account token stored in secret {0} to authenticate as service account {1}" + " in namespace {2}", secretName, serviceAccount, namespace); String secretToken = Secret.getSecretEncodedToken(namespace, secretName); - if (secretToken.isEmpty()) { + if (secretToken != null && secretToken.isEmpty()) { logger.info("Did not get encoded token for secret {0} associated with service account {1} in namespace {2}", secretName, serviceAccount, namespace); return null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/VerrazzanoUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/VerrazzanoUtils.java index 3bd63bcc19f..31538d8b21a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/VerrazzanoUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/VerrazzanoUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -52,6 +52,7 @@ public static void setLabelToNamespace(List namespaces) throws ApiExcept for (String namespace : namespaces) { V1Namespace namespaceObject = assertDoesNotThrow(() -> Kubernetes.getNamespace(namespace)); assertNotNull(namespaceObject, "Can't find namespace with name " + namespace); + assertNotNull(namespaceObject.getMetadata(), "namespace " + namespace + " metadata is null"); namespaceObject.getMetadata().setLabels(labels); assertDoesNotThrow(() -> replaceNamespace(namespaceObject)); } @@ -153,7 +154,8 @@ public static void createVzConfigmapComponent(String componentName, String confi testUntil(() -> { try { return Kubernetes.listComponents(namespace).getItems().stream() - .anyMatch(comp -> comp.getMetadata().getName().equals(componentName)); + .anyMatch(comp -> comp.getMetadata().getName() != null + && comp.getMetadata().getName().equals(componentName)); } catch (ApiException ex) { logger.warning(ex.getResponseBody()); } @@ -177,7 +179,8 @@ public static void deleteVzConfigmapComponent(String componentName, String names testUntil(() -> { try { return !Kubernetes.listComponents(namespace).getItems().stream() - .anyMatch(component -> component.getMetadata().getName().equals(componentName)); + .anyMatch(component -> component.getMetadata().getName() != null + && component.getMetadata().getName().equals(componentName)); } catch (ApiException ex) { logger.warning(ex.getResponseBody()); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/WLSTUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/WLSTUtils.java index f088a3c0de6..2a5c2cf3fc5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/WLSTUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/WLSTUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -143,7 +143,7 @@ public static void createWLSTJob(String wlstJobName, String wlstScriptConfigMapN // check job status and fail test if the job failed to execute WLST V1Job job = getJob(jobName, namespace); - if (job != null) { + if (job != null && job.getStatus() != null && job.getStatus().getConditions() != null) { V1JobCondition jobCondition = job.getStatus().getConditions().stream().filter( v1JobCondition -> "Failed".equals(v1JobCondition.getType())) .findAny() @@ -151,13 +151,13 @@ public static void createWLSTJob(String wlstJobName, String wlstScriptConfigMapN if (jobCondition != null) { logger.severe("Job {0} failed to execute WLST script", jobName); List pods = listPods(namespace, "job-name=" + jobName).getItems(); - if (!pods.isEmpty()) { + if (!pods.isEmpty() && pods.get(0).getMetadata() != null) { logger.severe(getPodLog(pods.get(0).getMetadata().getName(), namespace)); fail("WLST execute job failed"); } } List pods = listPods(namespace, "job-name=" + jobName).getItems(); - if (!pods.isEmpty()) { + if (!pods.isEmpty() && pods.get(0).getMetadata() != null) { logger.info(getPodLog(pods.get(0).getMetadata().getName(), namespace)); } } @@ -236,7 +236,9 @@ public static ExecResult executeWLSTScriptInImageContainer(String containerName, result = exec(checkImageBuilderVersion, true); logger.info(WLSIMG_BUILDER + " version: {0}", result.stdout()); } catch (Exception ex) { - logger.info(WLSIMG_BUILDER + " version failed error {0} and {1}", result.stderr(), ex.getMessage()); + if (result != null) { + logger.info(WLSIMG_BUILDER + " version failed error {0} and {1}", result.stderr(), ex.getMessage()); + } ex.printStackTrace(); } From d13f9a4c2a0213952b69c848caf04d7c3f88d7da Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 17 May 2024 14:10:10 -0400 Subject: [PATCH 067/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fffd8b531b5..aa70752d56b 100644 --- a/pom.xml +++ b/pom.xml @@ -720,7 +720,7 @@ 2.17.1 2.2 2.10.1 - 9.1.0 + 9.2.0 2.0.13 1.5.6 ${project.basedir}/src-generated-swagger From df11f4ac7381483f2b9df50027d442de6aefce89 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 17 May 2024 14:19:47 -0400 Subject: [PATCH 068/356] Operator 4.2.3 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index b16de6bb5ac..5925387091e 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 371d0bfb59e..d71f348ee95 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 43267feeee7..9d8b49e7851 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 1e2f346f7a6..1674d454809 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.3-SNAPSHOT + 4.2.3 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 35d52d62fa1..e6067508ddc 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 872578d4d76..b823d909ab9 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.3-SNAPSHOT + 4.2.3 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 28dec951c52..02d409766c0 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index aa70752d56b..01eb04f9aef 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 8395d7d4788..81e8834bf94 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3-SNAPSHOT + 4.2.3 operator-swagger From 43dccf1a45d0a37feb14d63b641c5b67ee3747f1 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 17 May 2024 15:52:31 -0400 Subject: [PATCH 069/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 5925387091e..4e2c65d7a4c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index d71f348ee95..841160073e2 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 9d8b49e7851..ccc7bef7dc4 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 1674d454809..8e8d4fb01b9 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.3 + 4.2.4-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index e6067508ddc..55578188e2f 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index b823d909ab9..e2040b1feca 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.3 + 4.2.4-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 02d409766c0..843a20afe4e 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 01eb04f9aef..f7c20022d99 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 81e8834bf94..1cce70deaef 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.3 + 4.2.4-SNAPSHOT operator-swagger From 6dc18b2a93a4ba7bd23d9d71cb7b9595583f7117 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 20 May 2024 14:11:51 +0000 Subject: [PATCH 070/356] Merge branch 'harden-aux-image-script' into 'main' adding starts with test for auxImage.sh See merge request weblogic-cloud/weblogic-kubernetes-operator!4714 (cherry picked from commit d93dd6eb9b9c17a6983936c041b08af9046f55b4) 63816332 adding starts with test for auxImage.sh --- operator/src/main/resources/scripts/auxImage.sh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/auxImage.sh b/operator/src/main/resources/scripts/auxImage.sh index e9a7823ab41..83e31052948 100755 --- a/operator/src/main/resources/scripts/auxImage.sh +++ b/operator/src/main/resources/scripts/auxImage.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Init container script for the auxiliary image feature. @@ -26,6 +26,21 @@ scriptDir="$( cd "$(dirname "$0")" > /dev/null 2>&1 ; pwd -P )" if [ "${debug}" == "true" ]; then set -x; fi; +# This script runs in the auxiliary image container, which is often using +# busybox or some other very sparse base image. This script relies on +# the determining that the shell supports the "starts with" test so +# make sure that it works and exit if it does not. + +SHELL_TEST=abcd +if [[ "${SHELL_TEST}" == "ab"* ]]; then + SHELL_IS_GOOD=true +fi + +if [ -z "${SHELL_IS_GOOD}" ]; then + echo "[SEVERE] The shell in the auxiliary image is missing required functionality. Exiting." + exit 1 +fi + . ${scriptDir}/utils_base.sh [ $? -ne 0 ] && echo "[SEVERE] Missing file ${scriptDir}/utils_base.sh" && exit 1 UNKNOWN_SHELL=true From e6cda4e36bd96b8c214633f116f400d2f04575bc Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 20 May 2024 10:28:25 -0400 Subject: [PATCH 071/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f7c20022d99..220187544d4 100644 --- a/pom.xml +++ b/pom.xml @@ -695,7 +695,7 @@ 0.1.0 2.9.0 0.9.6 - 3.5.0 + 3.6.0 1.0.0 3.25.3 2.16.1 @@ -719,7 +719,7 @@ 2.17.1 2.17.1 2.2 - 2.10.1 + 2.11.0 9.2.0 2.0.13 1.5.6 From c185761f2f4899660ae93a696ba17aaf5fc8f439 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 20 May 2024 19:32:05 +0000 Subject: [PATCH 072/356] Merge branch 'k8s-1.29' into 'main' Document certification for 1.29.1 See merge request weblogic-cloud/weblogic-kubernetes-operator!4715 (cherry picked from commit b1c25d23703fc3c537bd3c6f2455c7884ce850f1) bf36c12f Document certification for 1.29.1 --- .../site/content/introduction/prerequisites/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/introduction/prerequisites/introduction.md b/documentation/site/content/introduction/prerequisites/introduction.md index e41a296e678..e6ba2229106 100644 --- a/documentation/site/content/introduction/prerequisites/introduction.md +++ b/documentation/site/content/introduction/prerequisites/introduction.md @@ -7,7 +7,7 @@ weight: 5 For the current production release {{< latestVersion >}}: -* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, and 1.28.2+ (check with `kubectl version`). +* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, 1.28.2+, and 1.29.1+ (check with `kubectl version`). * Flannel networking v0.13.0-amd64 or later (check with `docker images | grep flannel`), Calico networking v3.16.1 or later, *or* OpenShift SDN on OpenShift 4.3 systems. * Docker 19.03.1+ (check with `docker version`) *or* CRI-O 1.20.2+ (check with `crictl version | grep RuntimeVersion`). From 771ea5b96d7f5bce6bc90e5ceac6e90b7bcafb27 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 21 May 2024 16:32:53 +0000 Subject: [PATCH 073/356] Fix failures in release/4.2 upgrade tests --- Jenkinsfile.podman | 2 +- .../ItOperatorUpgradeWithIstio.java | 31 +++- .../kubernetes/ItOperatorWlsUpgrade.java | 137 ++++++++++-------- .../weblogic/kubernetes/utils/DbUtils.java | 3 +- .../weblogic/kubernetes/utils/IstioUtils.java | 12 +- 5 files changed, 105 insertions(+), 80 deletions(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 5e2eeae2ebe..2e98e11261d 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -22,7 +22,7 @@ def kind_k8s_map = [ def _kind_image = null CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential - H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24''' + H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.17''' pipeline { agent { label 'large-ol9' } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java index 165901fe9e9..62cae38d43e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java @@ -1,8 +1,10 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Path; import java.nio.file.Paths; import java.time.OffsetDateTime; @@ -28,11 +30,16 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_COMPLETED_TYPE; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ISTIO_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OLD_DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.addLabelsToNamespace; @@ -40,6 +47,7 @@ import static oracle.weblogic.kubernetes.utils.CleanupUtil.cleanup; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.verifyPodsNotRolled; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; @@ -206,14 +214,25 @@ void upgradeWlsIstioDomain(String oldVersion) { // before upgrading to Latest verifyDomainStatusConditionTypeDoesNotExist(domainUid, domainNamespace, DOMAIN_STATUS_CONDITION_COMPLETED_TYPE, OLD_DOMAIN_VERSION); - istioIngressPort = - createIstioService(domainUid,clusterName,adminServerPodName,domainNamespace); - checkIstioService(istioIngressPort,domainNamespace); + istioIngressPort + = createIstioService(domainUid, clusterName, adminServerPodName, domainNamespace); + String istioHost = null; + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + try { + istioHost = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException ex) { + logger.severe(ex.getLocalizedMessage()); + } + istioIngressPort = ISTIO_HTTP_HOSTPORT; + } else { + istioHost = K8S_NODEPORT_HOST; + } + checkIstioService(istioHost, istioIngressPort, domainNamespace); upgradeOperatorToCurrent(opNamespace); - checkDomainStatus(domainNamespace,domainUid); + checkDomainStatus(domainNamespace, domainUid); verifyPodsNotRolled(domainNamespace, pods); // Re check the istio Service After Upgrade - checkIstioService(istioIngressPort,domainNamespace); + checkIstioService(istioHost, istioIngressPort, domainNamespace); } private void createSecrets() { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java index 9b4fcf13472..3dc8c512e03 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java @@ -1,8 +1,9 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; +import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -49,9 +50,11 @@ import static oracle.weblogic.kubernetes.TestConstants.DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_COMPLETED_TYPE; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MII_AUXILIARY_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; @@ -63,9 +66,12 @@ import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -77,12 +83,13 @@ import static oracle.weblogic.kubernetes.utils.ApplicationUtils.collectAppAvailability; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.deployAndAccessApplication; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminConsoleAccessible; +import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createPushAuxiliaryImageWithDomainConfig; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.verifyPodsNotRolled; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.startPortForwardProcess; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.stopPortForwardProcess; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; @@ -124,7 +131,9 @@ class ItOperatorWlsUpgrade { private static LoggingFacade logger = null; private String domainUid = "domain1"; - private String adminServerPodName = domainUid + "-admin-server"; + private String adminServerName = "admin-server"; + private int adminPort = 7001; + private String adminServerPodName = domainUid + "-" + adminServerName; private String managedServerPodNamePrefix = domainUid + "-managed-server"; private int replicaCount = 2; private List namespaces; @@ -137,6 +146,7 @@ class ItOperatorWlsUpgrade { private Path destDomainYaml = null; private static String miiAuxiliaryImageTag = "aux-explict-upgrade"; private static final String miiAuxiliaryImage = MII_AUXILIARY_IMAGE_NAME + ":" + miiAuxiliaryImageTag; + private static String hostHeader; /** * For each test: @@ -174,69 +184,82 @@ void testOperatorWlsUpgradeFrom338ToCurrent(String domainType) { } /** - * Operator upgrade from 3.4.8 to current. + * Operator upgrade from 3.4.12 to current. */ @ParameterizedTest - @DisplayName("Upgrade Operator from 3.4.8 to current") + @DisplayName("Upgrade Operator from 3.4.12 to current") @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom348ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom348ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "3.4.8", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + void testOperatorWlsUpgradeFrom3412ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom3412ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "3.4.12", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** - * Operator upgrade from 3.4.9 to current. + * Operator upgrade from 3.4.13 to current. */ @ParameterizedTest - @DisplayName("Upgrade Operator from 3.4.9 to current") + @DisplayName("Upgrade Operator from 3.4.13 to current") @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom349ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom346ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "3.4.9", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + void testOperatorWlsUpgradeFrom3413ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom3413ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "3.4.13", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } + + /** + * Operator upgrade from 4.0.8 to current. + */ + @ParameterizedTest + @DisplayName("Upgrade Operator from 4.0.8 to current") + @ValueSource(strings = { "Image", "FromModel" }) + void testOperatorWlsUpgradeFrom408ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom408ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "4.0.8", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + } + /** - * Operator upgrade from 4.0.7 to current. + * Operator upgrade from 4.0.9 to current. */ @ParameterizedTest - @DisplayName("Upgrade Operator from 4.0.7 to current") + @DisplayName("Upgrade Operator from 4.0.9 to current") @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom407ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom407ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.0.7", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + void testOperatorWlsUpgradeFrom409ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom409ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "4.0.9", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** - * Operator upgrade from 4.0.8 to current. + * Operator upgrade from 4.0.10 to current. */ @ParameterizedTest - @DisplayName("Upgrade Operator from 4.0.8 to current") + @DisplayName("Upgrade Operator from 4.0.10 to current") @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom406ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom408ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.0.8", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + void testOperatorWlsUpgradeFrom4010ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom4010ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "4.0.10", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } + /** - * Operator upgrade from 4.1.0 to current. + * Operator upgrade from 4.1.7 to current. */ @ParameterizedTest - @DisplayName("Upgrade Operator from 4.1.0 to current") + @DisplayName("Upgrade Operator from 4.1.7 to current") @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom410ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom410ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.1.0", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + void testOperatorWlsUpgradeFrom417ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom417ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "4.1.7", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** - * Operator upgrade from 4.1.1 to current. + * Operator upgrade from 4.1.8 to current. */ @ParameterizedTest - @DisplayName("Upgrade Operator from 4.1.1 to current") + @DisplayName("Upgrade Operator from 4.1.8 to current") @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom411ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom411ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.1.1", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + void testOperatorWlsUpgradeFrom418ToCurrent(String domainType) { + logger.info("Starting test testOperatorWlsUpgradeFrom418ToCurrent with domain type {0}", domainType); + installAndUpgradeOperator(domainType, "4.1.8", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** @@ -661,26 +684,6 @@ private void removePortForwardingAttribute( boolean result = Command.withParams(params).execute(); } - private void checkAdminPortForwarding(String domainNamespace, boolean successExpected) { - - logger.info("Checking port forwarding [{0}]", successExpected); - String forwardPort = - startPortForwardProcess("localhost", domainNamespace, - domainUid, 7001); - assertNotNull(forwardPort, "port-forward fails to assign local port"); - logger.info("Forwarded admin-port is {0}", forwardPort); - if (successExpected) { - verifyAdminConsoleAccessible(domainNamespace, "localhost", - forwardPort, false); - logger.info("ready app is accessible thru port forwarding"); - } else { - verifyAdminConsoleAccessible(domainNamespace, "localhost", - forwardPort, false, false); - logger.info("WebLogic console shouldn't accessible thru port forwarding"); - } - stopPortForwardProcess(domainNamespace); - } - /** * Replace the fields in domain yaml file with testing attributes. * For example, namespace, domainUid, and image. Then create domain using @@ -754,17 +757,23 @@ private void createWlsDomainAndVerifyByDomainYaml(String domainType, private void verifyDomain(String domainUidString, String domainNamespace, String externalServiceNameSuffix) { checkDomainStarted(domainUid, domainNamespace); + + if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + hostHeader = createIngressHostRouting(domainNamespace, domainUidString, adminServerName, adminPort); + assertDoesNotThrow(() -> verifyAdminServerRESTAccess(formatIPv6Host(InetAddress.getLocalHost().getHostAddress()), + TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); + } - logger.info("Getting node port for default channel"); - int serviceNodePort = assertDoesNotThrow(() -> getServiceNodePort( - domainNamespace, getExternalServicePodName(adminServerPodName, externalServiceNameSuffix), "default"), - "Getting admin server node port failed"); - logger.info("Got node port {0} for default channel for domainNameSpace {1}", serviceNodePort, domainNamespace); - - logger.info("Validating WebLogic admin server access by login to console"); - verifyAdminConsoleAccessible(domainNamespace, K8S_NODEPORT_HOST, - String.valueOf(serviceNodePort), false); - + if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + logger.info("Getting node port for default channel"); + int serviceNodePort = assertDoesNotThrow(() -> getServiceNodePort( + domainNamespace, getExternalServicePodName(adminServerPodName, externalServiceNameSuffix), "default"), + "Getting admin server node port failed"); + logger.info("Got node port {0} for default channel for domainNameSpace {1}", serviceNodePort, domainNamespace); + logger.info("Validating WebLogic admin server access by login to console"); + verifyAdminConsoleAccessible(domainNamespace, K8S_NODEPORT_HOST, + String.valueOf(serviceNodePort), false); + } } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java index 2337e61a6a5..b2e7b9adfb3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java @@ -276,8 +276,7 @@ public static synchronized void startOracleDB(String dbBaseImageName, int dbPort .protocol("TCP") .hostPort(dbListenerPort))) .resources(new V1ResourceRequirements() - .limits(limits) - .requests(requests)) + .limits(limits)) .terminationMessagePath("/dev/termination-log") .terminationMessagePolicy("File"))) .dnsPolicy("ClusterFirst") diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index 759c2dc62f1..9b0c167b8b0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -35,7 +35,6 @@ import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.ISTIO_VERSION; -import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; @@ -51,6 +50,7 @@ import static oracle.weblogic.kubernetes.utils.ApplicationUtils.checkAppUsingHostHeader; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; @@ -489,19 +489,17 @@ public static AdminServer createAdminServer() { /** - * Check WebLogic console thru Istio Ingress Port. + * Check WebLogic access through Istio Ingress Port. + * @param istioHost Host * @param istioIngressPort Istio Ingress Port * @param domainNamespace Domain namespace that the domain is hosted */ - public static void checkIstioService(int istioIngressPort, String domainNamespace) { + public static void checkIstioService(String istioHost, int istioIngressPort, String domainNamespace) { // We can not verify Rest Management console thru Administration NodePort // in istio, as we can not enable Administration NodePort LoggingFacade logger = getLogger(); logger.info("Verifying Istio Service @IngressPort [{0}]", istioIngressPort); - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } + String host = formatIPv6Host(istioHost); String readyAppUrl = "http://" + host + ":" + istioIngressPort + "/weblogic/ready"; boolean checlReadyApp = checkAppUsingHostHeader(readyAppUrl, domainNamespace + ".org"); From 5da3c948bf9de55a5b97eaab0cdf106820148e80 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Wed, 22 May 2024 16:23:57 +0000 Subject: [PATCH 074/356] Fix for FMW tests in kind-sequential --- .../test/java/oracle/weblogic/kubernetes/utils/DbUtils.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java index b2e7b9adfb3..5ccf76afdbc 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java @@ -41,7 +41,6 @@ import io.kubernetes.client.openapi.models.V1PodSpec; import io.kubernetes.client.openapi.models.V1PodTemplateSpec; import io.kubernetes.client.openapi.models.V1PolicyRule; -import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1Role; import io.kubernetes.client.openapi.models.V1RoleBinding; import io.kubernetes.client.openapi.models.V1RoleRef; @@ -275,8 +274,6 @@ public static synchronized void startOracleDB(String dbBaseImageName, int dbPort .name("tns") .protocol("TCP") .hostPort(dbListenerPort))) - .resources(new V1ResourceRequirements() - .limits(limits)) .terminationMessagePath("/dev/termination-log") .terminationMessagePolicy("File"))) .dnsPolicy("ClusterFirst") From 14c97c6c22466aa55cb1cd0e1f3a02e30f955992 Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Thu, 23 May 2024 18:33:52 +0000 Subject: [PATCH 075/356] [OKE] [backport] internal OKE conversion in OWLS-115531 on main into release/4.2 --- .../weblogic/kubernetes/ItDedicatedMode.java | 13 +- .../kubernetes/ItMiiDynamicUpdatePart3.java | 12 +- .../kubernetes/ItMultiDomainModelsScale.java | 188 +++++++++++++----- .../weblogic/kubernetes/ItT3Channel.java | 115 +++++++---- 4 files changed, 232 insertions(+), 96 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java index 703c5beba3e..f3326916693 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java @@ -37,6 +37,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; @@ -46,6 +47,7 @@ import static oracle.weblogic.kubernetes.actions.impl.Domain.scaleClusterWithRestApi; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.scaleAndVerifyCluster; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyClusterAfterScaling; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; @@ -66,7 +68,7 @@ */ @DisplayName("Test Operator and WebLogic domain with Dedicated set to true") @Tag("kind-sequential") -@Tag("oke-sequential") +@Tag("oke-gate") @Tag("okd-wls-mrg") @IntegrationTest class ItDedicatedMode { @@ -208,8 +210,13 @@ void testDedicatedModeSameNamespace() { } logger.info("scaling the cluster from {0} servers to {1} servers", replicaCount, replicaCount + 1); - scaleClusterWithRestApi(domainUid, clusterName, replicaCount + 1, - externalRestHttpshost, externalRestHttpsPort, opNamespace, opServiceAccount); + if (OKE_CLUSTER) { + scaleAndVerifyCluster(clusterResName, domainUid, domain1Namespace, managedServerPodPrefix, + replicaCount, replicaCount + 1, null, null); + } else { + scaleClusterWithRestApi(domainUid, clusterName, replicaCount + 1, + externalRestHttpshost, externalRestHttpsPort, opNamespace, opServiceAccount); + } verifyClusterAfterScaling(domainUid, domain1Namespace, managedServerPodPrefix, replicaCount, replicaCount + 1, null, null, listOfPodCreationTimestamp); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java index 662f24df3fa..467c11778e9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java @@ -78,7 +78,7 @@ @DisplayName("Test dynamic updates to a model in image domain, part3") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-sequential") +@Tag("oke-gate") @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-mrg") @@ -302,8 +302,7 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() { // This test uses the WebLogic domain created in BeforeAll method // BeforeEach method ensures that the server pods are running - LinkedHashMap pods = - helper.addDataSourceAndVerify(false); + LinkedHashMap pods = helper.addDataSourceAndVerify(false); // Replace contents of an existing configMap with cm config and application target as // there are issues with removing them, WDT-535 @@ -328,7 +327,6 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() { verifyPodIntrospectVersionUpdated(pods.keySet(), introspectVersion, helper.domainNamespace); - // check datasource configuration using REST api if (OKE_CLUSTER) { assertTrue(checkSystemResourceConfigViaAdminPod(helper.adminServerPodName, helper.domainNamespace, @@ -377,8 +375,7 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() { // write sparse yaml to delete datasource to file, delete ds to keep the config clean Path pathToDeleteDSYaml = Paths.get(WORK_DIR + "/deleteds.yaml"); - String yamlToDeleteDS = "resources:\n" - + " JDBCSystemResource:\n"; + String yamlToDeleteDS = "resources:\n" + " JDBCSystemResource:\n"; assertDoesNotThrow(() -> Files.write(pathToDeleteDSYaml, yamlToDeleteDS.getBytes())); @@ -426,12 +423,9 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() { } logger.info("JDBCSystemResource Datasource is deleted"); - - // check that the domain status condition contains the correct type and expected status logger.info("verifying the domain status condition contains the correct type and expected status"); helper.verifyDomainStatusConditionNoErrorMsg("Completed", "True"); - } private void verifyIntrospectorFailsWithExpectedErrorMsg(String expectedErrorMsg) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java index 3eac8512d06..73f0b16957b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java @@ -30,6 +30,7 @@ import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.DomainUtils; +import oracle.weblogic.kubernetes.utils.ExecResult; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; @@ -48,7 +49,9 @@ import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.OKD; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_EXTERNAL_REST_HTTPSPORT; +import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; @@ -61,6 +64,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.buildAppArchive; import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; import static oracle.weblogic.kubernetes.actions.TestActions.getDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.TestActions.getOperatorPodName; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.getServicePort; import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; @@ -73,8 +77,10 @@ import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.exeAppInServerPod; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.scaleAndVerifyCluster; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.startPortForwardProcess; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.stopPortForwardProcess; @@ -97,6 +103,7 @@ import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -114,7 +121,7 @@ + "rolling restart behavior in a multi-cluster MII domain and " + "the sample application can be accessed via NGINX ingress controller") @Tag("kind-sequential") -@Tag("oke-sequential") +@Tag("oke-gate") @IntegrationTest class ItMultiDomainModelsScale { @@ -140,6 +147,7 @@ class ItMultiDomainModelsScale { private static String opNamespace = null; private static String opServiceAccount = null; private static NginxParams nginxHelmParams = null; + private static String nginxNamespace = null; private static int nodeportshttp = 0; private static LoggingFacade logger = null; private static String miiDomainNamespace = null; @@ -170,7 +178,7 @@ public static void initAll(@Namespaces(5) List namespaces) { // get a unique NGINX namespace logger.info("Get a unique namespace for NGINX"); assertNotNull(namespaces.get(1), "Namespace list is null"); - String nginxNamespace = namespaces.get(1); + nginxNamespace = namespaces.get(1); // get unique namespaces for three different type of domains logger.info("Getting unique namespaces for three different type of domains"); @@ -245,29 +253,51 @@ void testScaleClustersByPatchingClusterResource(String domainType) { numberOfServers = 3; } - logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers.", - clusterName, domainUid, domainNamespace, numberOfServers); - curlCmd = generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT); - List managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, replicaCount); - scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, - replicaCount, numberOfServers, curlCmd, managedServersBeforeScale); - - // then scale cluster back to 1 servers - logger.info("Scaling cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", - clusterName, domainUid, domainNamespace, numberOfServers, replicaCount); - managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, numberOfServers); - scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, - numberOfServers, replicaCount, curlCmd, managedServersBeforeScale); + if (OKE_CLUSTER) { + logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers.", + clusterName, domainUid, domainNamespace, numberOfServers); + scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, + replicaCount, numberOfServers, null, null); + + // then scale cluster back to 1 servers + logger.info("Scaling cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", + clusterName, domainUid, domainNamespace, numberOfServers, replicaCount); + scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, + numberOfServers, replicaCount, null, null); + } else { + logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers.", + clusterName, domainUid, domainNamespace, numberOfServers); + curlCmd = generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT); + List managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, replicaCount); + scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, + replicaCount, numberOfServers, curlCmd, managedServersBeforeScale); + + // then scale cluster back to 1 servers + logger.info("Scaling cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", + clusterName, domainUid, domainNamespace, numberOfServers, replicaCount); + managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, numberOfServers); + scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, + numberOfServers, replicaCount, curlCmd, managedServersBeforeScale); + } } // verify admin console login - if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + if (OKE_CLUSTER) { + String resourcePath = "/console/login/LoginForm.jsp"; + final String adminServerPodName = domainUid + "-admin-server"; + ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, ADMIN_SERVER_PORT, resourcePath); + logger.info("result in OKE_CLUSTER is {0}", result.toString()); + assertEquals(0, result.exitValue(), "Failed to access WebLogic console"); + + // verify admin console login using ingress controller + verifyReadyAppUsingIngressController(domainUid, domainNamespace); + } else if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostHeader = createIngressHostRoutingIfNotExists(domainNamespace, domainUid); assertDoesNotThrow(() -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); - // verify ready app using ingress controller + // verify admin console login using ingress controller verifyReadyAppUsingIngressController(domainUid, domainNamespace); } @@ -305,14 +335,26 @@ void testScaleClustersWithRestApi(String domainType) { String clusterName = domain.getSpec().getClusters().get(0).getName(); String managedServerPodNamePrefix = generateMsPodNamePrefix(numClusters, domainUid, clusterName); int numberOfServers = 3; + String operatorPodName = null; + curlCmd = generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT); + + if (OKE_CLUSTER) { + // get operator pod name + operatorPodName = assertDoesNotThrow(() -> getOperatorPodName(OPERATOR_RELEASE_NAME, opNamespace)); + assertNotNull(operatorPodName, "Operator pod name returned is null"); + logger.info("Operator pod name {0}", operatorPodName); + curlCmd = domainType.contains("modelInImage") + ? generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT) : null; + } logger.info("Scaling cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", clusterName, domainUid, domainNamespace, replicaCount, numberOfServers); - curlCmd = generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT); + //curlCmd = generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT); List managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, replicaCount); scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, replicaCount, numberOfServers, true, OPERATOR_EXTERNAL_REST_HTTPSPORT, opNamespace, opServiceAccount, - false, "", "", 0, "", "", curlCmd, managedServersBeforeScale); + false, "", "", 0, "", "", + curlCmd, managedServersBeforeScale, operatorPodName); // then scale cluster back to 2 servers logger.info("Scaling cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", @@ -320,16 +362,26 @@ void testScaleClustersWithRestApi(String domainType) { managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, numberOfServers); scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, numberOfServers, replicaCount, true, OPERATOR_EXTERNAL_REST_HTTPSPORT, opNamespace, opServiceAccount, - false, "", "", 0, "", "", curlCmd, managedServersBeforeScale); + false, "", "", 0, "", "", + curlCmd, managedServersBeforeScale, operatorPodName); // verify admin console login - if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + if (OKE_CLUSTER) { + String resourcePath = "/console/login/LoginForm.jsp"; + final String adminServerPodName = domainUid + "-admin-server"; + ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName,ADMIN_SERVER_PORT, resourcePath); + logger.info("result in OKE_CLUSTER is {0}", result.toString()); + assertEquals(0, result.exitValue(), "Failed to access WebLogic console"); + + // verify admin console login using ingress controller + verifyReadyAppUsingIngressController(domainUid, domainNamespace); + } else if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostHeader = createIngressHostRoutingIfNotExists(domainNamespace, domainUid); assertDoesNotThrow(() -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); - // verify ready app using ingress controller + // verify admin console login using ingress controller verifyReadyAppUsingIngressController(domainUid, domainNamespace); } @@ -341,6 +393,10 @@ void testScaleClustersWithRestApi(String domainType) { * Scale cluster using WLDF policy for three different type of domains. * i.e. domain-on-pv, domain-in-image and model-in-image * + * In internal OKE env, we only test scaling cluster using WLDF policy for domain type, model-in-image. + * domain type, domain-in-image is excluded and domain type, domain-on-pv is tested in + * ItMultiDomainModelsScaleWithWLDFDomainOnPV.java + * * @param domainType domain type, possible value: modelInImage, domainInImage, domainOnPV */ @ParameterizedTest @@ -361,7 +417,13 @@ void testScaleClustersWithWLDF(String domainType) { String managedServerPodNamePrefix = generateMsPodNamePrefix(numClusters, domainUid, clusterName); curlCmd = generateCurlCmd(domainUid, domainNamespace, clusterName, SAMPLE_APP_CONTEXT_ROOT); - logger.info("BR: curlCmd = {0}", curlCmd); + logger.info("Generated curlCmd = {0}", curlCmd); + + // domain type, domain-in-image is excluded and domain type, domain-on-pv is tested in + // ItMultiDomainModelsScaleWithWLDFDomainOnPV.java + if (OKE_CLUSTER && (domainType.contains("domainInImage") || domainType.contains("domainOnPV"))) { + return; + } // scale up the cluster by 1 server logger.info("Scaling cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", @@ -369,7 +431,7 @@ void testScaleClustersWithWLDF(String domainType) { List managedServersBeforeScale = listManagedServersBeforeScale(numClusters, clusterName, replicaCount); String curlCmdForWLDFScript = generateCurlCmd(domainUid, domainNamespace, clusterName, WLDF_OPENSESSION_APP_CONTEXT_ROOT); - logger.info("BR: curlCmdForWLDFScript = {0}", curlCmdForWLDFScript); + logger.info("Generated: curlCmdForWLDFScript = {0}", curlCmdForWLDFScript); scaleAndVerifyCluster(clusterName, domainUid, domainNamespace, managedServerPodNamePrefix, replicaCount, replicaCount + 1, false, OPERATOR_EXTERNAL_REST_HTTPSPORT, opNamespace, opServiceAccount, @@ -387,13 +449,22 @@ void testScaleClustersWithWLDF(String domainType) { WLDF_OPENSESSION_APP, curlCmdForWLDFScript, curlCmd, managedServersBeforeScale); // verify admin console login - if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + if (OKE_CLUSTER) { + String resourcePath = "/console/login/LoginForm.jsp"; + final String adminServerPodName = domainUid + "-admin-server"; + ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName,ADMIN_SERVER_PORT, resourcePath); + logger.info("result in OKE_CLUSTER is {0}", result.toString()); + assertEquals(0, result.exitValue(), "Failed to access WebLogic console"); + + // verify admin console login using ingress controller + verifyReadyAppUsingIngressController(domainUid, domainNamespace); + } else if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostHeader = createIngressHostRoutingIfNotExists(domainNamespace, domainUid); assertDoesNotThrow(() -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); - // verify ready app using ingress controller + // verify admin console login using ingress controller verifyReadyAppUsingIngressController(domainUid, domainNamespace); } @@ -575,22 +646,30 @@ private static DomainResource createDomainOnPvUsingWdt(String domainUid, String * @param appContextRoot the context root of the application * @return curl command string */ - private static String generateCurlCmd(String domainUid, String domainNamespace, String clusterName, + private static String generateCurlCmd(String domainUid, + String domainNamespace, + String clusterName, String appContextRoot) { if (OKD) { String routeHost = getRouteHost(domainNamespace, domainUid + "-cluster-" + clusterName); logger.info("routeHost = {0}", routeHost); - return String.format("curl -g -v --show-error --noproxy '*' http://%s/%s/index.jsp", - routeHost, appContextRoot); - + return String.format("curl -g -v --show-error --noproxy '*' http://%s/%s/index.jsp", routeHost, appContextRoot); } else { String host = K8S_NODEPORT_HOST; if (host.contains(":")) { host = "[" + host + "]"; } - return String.format("curl -g -v --show-error --noproxy '*' -H 'host: %s' http://%s/%s/index.jsp", + if (OKE_CLUSTER) { + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + + return String.format("curl -g -v --show-error --noproxy '*' -H 'host: %s' http://%s/%s/index.jsp", domainUid + "." + domainNamespace + "." + clusterName + ".test", - getHostAndPort(host, nodeportshttp), appContextRoot); + getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace), appContextRoot); + } else { + return String.format("curl -g -v --show-error --noproxy '*' -H 'host: %s' http://%s/%s/index.jsp", + domainUid + "." + domainNamespace + "." + clusterName + ".test", + getHostAndPort(host, nodeportshttp), appContextRoot); + } } } @@ -740,7 +819,10 @@ private static void createRouteForOKDOrIngressForDomain(DomainResource domain) { if (!OKD) { logger.info("Creating ingress for domain {0} in namespace {1}", domainUid, domainNamespace); - if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + if (OKE_CLUSTER) { + createIngressForDomainAndVerify(domainUid, domainNamespace, 0, clusterNameMsPortMap, + false, nginxHelmParams.getIngressClassName(), false, 0); + } else if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { createIngressForDomainAndVerify(domainUid, domainNamespace, nodeportshttp, clusterNameMsPortMap, true, nginxHelmParams.getIngressClassName(), true, ADMIN_SERVER_PORT); } else { @@ -795,7 +877,7 @@ private static void startDomainAndVerify(String domainNamespace, } } - // verify the ready app using admin node port + // verify the admin console login using admin node port private void verifyReadyAppUsingAdminNodePort(String domainUid, String domainNamespace) { String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; @@ -816,26 +898,34 @@ private void verifyReadyAppUsingAdminNodePort(String domainUid, String domainNam "readyapp validation"); } - // Verify ready app using ingress controller + // Verify admin console login using ingress controller private void verifyReadyAppUsingIngressController(String domainUid, String domainNamespace) { if (!OKD) { + if (OKE_CLUSTER) { + final String adminServerPodName = domainUid + "-admin-server"; + String resourcePath = "/weblogic/ready"; + ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7002, resourcePath); + logger.info("result in OKE_CLUSTER is {0}", result.toString()); + assertEquals(0, result.exitValue(), "Failed to access WebLogic ready app"); + } else { + String host = K8S_NODEPORT_HOST; + if (host.contains(":")) { + host = "[" + host + "]"; + } - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; + String curlCmd = "curl -g --silent --show-error --noproxy '*' -H 'host: " + + domainUid + "." + domainNamespace + ".adminserver.test" + + "' http://" + host + ":" + nodeportshttp + + "/weblogic/ready --write-out %{http_code} -o /dev/null"; + + logger.info("Executing curl command {0}", curlCmd); + testUntil(() -> callWebAppAndWaitTillReady(curlCmd, 5), + logger, + "Ready app on domain {0} in namespace {1} is accessible", + domainUid, + domainNamespace); } - String curlCmd = "curl -g --silent --show-error --noproxy '*' -H 'host: " - + domainUid + "." + domainNamespace + ".adminserver.test" - + "' http://" + host + ":" + nodeportshttp - + "/weblogic/ready --write-out %{http_code} -o /dev/null"; - - logger.info("Executing curl command {0}", curlCmd); - testUntil(() -> callWebAppAndWaitTillReady(curlCmd, 5), - logger, - "Ready app on domain {0} in namespace {1} is accessible", - domainUid, - domainNamespace); logger.info("Ready app on domain1 is accessible"); } else { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 42185fd1d9b..1af3451f4b8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.net.http.HttpResponse; import java.nio.file.Path; import java.nio.file.Paths; @@ -32,6 +33,8 @@ import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.BuildApplication; +import oracle.weblogic.kubernetes.utils.ExecCommand; +import oracle.weblogic.kubernetes.utils.ExecResult; import oracle.weblogic.kubernetes.utils.OracleHttpClient; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; @@ -46,7 +49,9 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -82,7 +87,7 @@ @DisplayName("Test T3 channel deployment") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-sequential") +@Tag("oke-gate") @Tag("kind-sequential") class ItT3Channel { // namespace constants @@ -164,6 +169,7 @@ void testAdminServerT3Channel() { // create a temporary WebLogic domain property file File domainPropertiesFile = assertDoesNotThrow(() -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), + //File.createTempFile("domain", "properties"), "Failed to create domain properties file"); Properties p = new Properties(); p.setProperty("domain_path", "/shared/" + domainNamespace + "/domains"); @@ -272,11 +278,21 @@ void testAdminServerT3Channel() { // deploy application and verify all servers functions normally //deploy clusterview application - logger.info("Deploying clusterview app {0} to cluster {1}", - clusterViewAppPath, clusterName); - deployUsingWlst(adminServerPodName, Integer.toString(t3ChannelPort), - ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, adminServerName + "," + clusterName, clusterViewAppPath, - domainNamespace); + if (OKE_CLUSTER) { + int adminPort = 7001; + assertDoesNotThrow(() -> deployUsingWlst(adminServerPodName, + String.valueOf(adminPort), + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, + clusterName + "," + adminServerName, + clusterViewAppPath, + domainNamespace), "Deploying the application"); + } else { + logger.info("Deploying clusterview app {0} to cluster {1}", + clusterViewAppPath, clusterName); + deployUsingWlst(adminServerPodName, Integer.toString(t3ChannelPort), + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, adminServerName + "," + clusterName, + clusterViewAppPath, domainNamespace); + } List managedServerNames = new ArrayList(); for (int i = 1; i <= replicaCount; i++) { @@ -317,41 +333,70 @@ void testAdminServerT3Channel() { } private static void verifyMemberHealth(String adminServerPodName, List managedServerNames, - String user, String code) { - + String user, String code) { logger.info("Checking the health of servers in cluster"); testUntil(() -> { - logger.info("Getting node port for default channel"); - int serviceNodePort = assertDoesNotThrow(() - -> getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"), - "Getting admin server node port failed"); - String host = K8S_NODEPORT_HOST; - String hostAndPort = host + ":" + serviceNodePort; - Map headers = null; - if (TestConstants.KIND_CLUSTER - && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; - headers = new HashMap<>(); - headers.put("host", hostHeader); - } - String url = "http://" + hostAndPort - + "/clusterview/ClusterViewServlet?user=" + user + "&password=" + code; - HttpResponse response; - response = OracleHttpClient.get(url, headers, true); - - boolean health = true; - for (String managedServer : managedServerNames) { - health = health && response.body().contains(managedServer + ":HEALTH_OK"); - if (health) { - logger.info(managedServer + " is healthy"); - } else { - logger.info(managedServer + " health is not OK or server not found"); + if (OKE_CLUSTER) { + // In internal OKE env, verifyMemberHealth in admin server pod + int adminPort = 7001; + final String command = KUBERNETES_CLI + " exec -n " + + domainNamespace + " " + adminServerPodName + " -- curl http://" + + adminServerPodName + ":" + + adminPort + "/clusterview/ClusterViewServlet" + + "\"?user=" + user + + "&password=" + code + "\""; + + ExecResult result = null; + try { + result = ExecCommand.exec(command, true); + } catch (IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + + String response = result.stdout().trim(); + logger.info(response); + boolean health = true; + for (String managedServer : managedServerNames) { + health = health && response.contains(managedServer + ":HEALTH_OK"); + if (health) { + logger.info(managedServer + " is healthy"); + } else { + logger.info(managedServer + " health is not OK or server not found"); + } + } + return health; + } else { + logger.info("Getting node port for default channel"); + int serviceNodePort = assertDoesNotThrow(() + -> getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"), + "Getting admin server node port failed"); + String host = K8S_NODEPORT_HOST; + String hostAndPort = host + ":" + serviceNodePort; + Map headers = null; + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; + headers = new HashMap<>(); + headers.put("host", hostHeader); } + String url = "http://" + hostAndPort + + "/clusterview/ClusterViewServlet?user=" + user + "&password=" + code; + HttpResponse response; + response = OracleHttpClient.get(url, headers, true); + + boolean health = true; + for (String managedServer : managedServerNames) { + health = health && response.body().contains(managedServer + ":HEALTH_OK"); + if (health) { + logger.info(managedServer + " is healthy"); + } else { + logger.info(managedServer + " health is not OK or server not found"); + } + } + return health; } - return health; }, logger, "Verifying the health of all cluster members"); } - } From a382dd041aa7e1e678e0b91582ff8c26429478b0 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 24 May 2024 13:06:13 +0000 Subject: [PATCH 076/356] Fix for ephemral-storage issue with kind cluster running in rootless podman --- Jenkinsfile.podman | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 2e98e11261d..92b6b3b29e2 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -474,6 +474,10 @@ nodes: extraMounts: - hostPath: ${pv_root} containerPath: ${pv_root} +kubeadmConfigPatches: +- | + kind: KubeletConfiguration + localStorageCapacityIsolation: true EOF export KUBECONFIG=${kubeconfig_file} From 94a0139ea8bf24fa746c2d62baf8b8e9ce29b6b2 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 29 May 2024 16:34:39 -0400 Subject: [PATCH 077/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 220187544d4..32f03bcb362 100644 --- a/pom.xml +++ b/pom.xml @@ -678,7 +678,7 @@ 3.6.1 3.5.3 3.2.0 - 10.16.0 + 10.17.0 1.0 3.3.2 3.2.4 @@ -687,7 +687,7 @@ 2.0.1 2.0.1 1.0.39 - 1.7.0 + 1.8.0 1.4.0 1.4.0 1.17.0 @@ -697,7 +697,7 @@ 0.9.6 3.6.0 1.0.0 - 3.25.3 + 3.26.0 2.16.1 4.2.1 19.0.1 @@ -711,7 +711,7 @@ 1.7.0 1.3.2 UTF-8 - 3.1.6 + 3.1.7 1.1.6 4.0.2 6.0.0 From 6098537a23d9ec51187eb071c814d744641c01d4 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 31 May 2024 17:40:03 +0000 Subject: [PATCH 078/356] Install Database operator once and use it for all tests --- .../weblogic/kubernetes/ItVzDBOperator.java | 7 +------ .../kubernetes/ItCrossDomainTransaction.java | 4 ---- .../oracle/weblogic/kubernetes/ItDBOperator.java | 7 +------ .../kubernetes/ItFmwDomainInPVUsingWDT.java | 5 +---- .../kubernetes/ItFmwDomainInPVUsingWLST.java | 5 +---- .../kubernetes/ItFmwDynamicDomainInPV.java | 5 +---- .../kubernetes/ItIstioCrossDomainTransaction.java | 6 ++---- .../weblogic/kubernetes/ItIstioDBOperator.java | 7 +------ .../oracle/weblogic/kubernetes/TestConstants.java | 2 ++ .../kubernetes/extensions/InitializationTasks.java | 14 +++++++++++++- .../oracle/weblogic/kubernetes/utils/DbUtils.java | 8 ++++---- 11 files changed, 27 insertions(+), 43 deletions(-) diff --git a/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzDBOperator.java b/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzDBOperator.java index bcd62962e29..a5017d75f0a 100644 --- a/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzDBOperator.java +++ b/integration-tests/src/test/java/oracle/verrazzano/weblogic/kubernetes/ItVzDBOperator.java @@ -104,8 +104,6 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.deleteOracleDB; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; -import static oracle.weblogic.kubernetes.utils.DbUtils.uninstallDBOperator; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; @@ -220,10 +218,8 @@ public static void initAll(@Namespaces(4) List namespaces) throws ApiExc createBaseRepoSecret(fmwDomainNamespace); createBaseRepoSecret(wlsDomainNamespace); - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(dbNamespace), "Failed to install database operator"); - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " @@ -649,7 +645,6 @@ private void testMiiJmsJtaServiceMigration() { public void tearDownAll() throws ApiException { if (!SKIP_CLEANUP) { deleteOracleDB(dbNamespace, dbName); - uninstallDBOperator(dbNamespace); Kubernetes.deletePv(pvName); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index 8d74bc7b280..0eebd72a735 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -82,7 +82,6 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFolder; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; @@ -187,9 +186,6 @@ public static void initAll(@Namespaces(4) List namespaces) throws Unknow ORACLEDBSUFFIX = ".svc.cluster.local:" + dbListenerPort + "/devpdb.k8s"; dbUrl = ORACLEDBURLPREFIX + domain2Namespace + ORACLEDBSUFFIX; createBaseRepoSecret(domain2Namespace); - - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(domain2Namespace), "Failed to install database operator"); logger.info("Create Oracle DB in namespace: {0} ", domain2Namespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, SYSPASSWORD, domain2Namespace)); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java index 8a46051012e..144aa1b7d03 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java @@ -73,8 +73,6 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.deleteOracleDB; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; -import static oracle.weblogic.kubernetes.utils.DbUtils.uninstallDBOperator; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; @@ -187,9 +185,7 @@ public static void initAll(@Namespaces(4) List namespaces) { // this secret is used only for non-kind cluster createBaseRepoSecret(fmwDomainNamespace); createBaseRepoSecret(wlsDomainNamespace); - - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(dbNamespace), "Failed to install database operator"); + createBaseRepoSecret(dbNamespace); logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); @@ -521,7 +517,6 @@ private void testMiiJmsJtaServiceMigration() { public void tearDownAll() throws ApiException { if (!SKIP_CLEANUP) { deleteOracleDB(dbNamespace, dbName); - uninstallDBOperator(dbNamespace); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java index 76dc9c10fed..6fcfa5353c7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java @@ -40,7 +40,6 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSecretWithUsernamePassword; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.FmwUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; @@ -124,10 +123,8 @@ public static void initAll(@Namespaces(3) List namespaces) { DOMAINHOMEPREFIX = "/shared/" + domainNamespace + "/domains/"; - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(dbNamespace), "Failed to install database operator"); - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index df1450ee021..d9b83d04e6b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -51,7 +51,6 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSecretWithUsernamePassword; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; @@ -122,10 +121,8 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace is null"); jrfDomainNamespace = namespaces.get(2); - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(dbNamespace), "Failed to install database operator"); - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java index 32858aa3ac1..492fdac3b11 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java @@ -50,7 +50,6 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSecretWithUsernamePassword; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyEMconsoleAccess; @@ -130,10 +129,8 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace is null"); domainNamespace = namespaces.get(2); - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(dbNamespace), "Failed to install database operator"); - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java index 184114b095f..895afe83ff0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java @@ -65,11 +65,11 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFolder; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createImageAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; @@ -148,10 +148,8 @@ public static void initAll(@Namespaces(3) List namespaces) throws Unknow assertNotNull(namespaces.get(2), "Namespace list is null"); domain2Namespace = namespaces.get(2); - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(domain2Namespace), "Failed to install database operator"); - logger.info("Create Oracle DB in namespace: {0} ", domain2Namespace); + createBaseRepoSecret(domain2Namespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, SYSPASSWORD, domain2Namespace)); // Now that we got the namespaces for both the domains, we need to update the model properties diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java index d8153f93e86..b6dff360fc4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java @@ -81,8 +81,6 @@ import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.deleteOracleDB; -import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; -import static oracle.weblogic.kubernetes.utils.DbUtils.uninstallDBOperator; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; @@ -181,10 +179,8 @@ public static void initAll(@Namespaces(4) List namespaces) { assertDoesNotThrow(() -> addLabelsToNamespace(wlsDomainNamespace, labelMap)); assertDoesNotThrow(() -> addLabelsToNamespace(opNamespace, labelMap)); - //install Oracle Database Operator - assertDoesNotThrow(() -> installDBOperator(dbNamespace), "Failed to install database operator"); - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); // create testwebapp.war @@ -385,7 +381,6 @@ private void testMiiJmsJtaServiceMigration() throws UnknownHostException { public void tearDownAll() throws ApiException { if (!SKIP_CLEANUP) { deleteOracleDB(dbNamespace, dbName); - uninstallDBOperator(dbNamespace); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index bcc99c38abd..d4268747962 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -476,6 +476,8 @@ public interface TestConstants { public static final String LARGE_DOMAIN_TESTING_PROPS_FILE = "largedomaintesting.props"; + public static final String ORACLE_OPERATOR_NS = "ns-oracle-operator"; + //node ports used by the integration tests public static final int IT_EXTERNALNODEPORTSERVICE_NODEPORT = 31000; public static final int IT_EXTERNALNODEPORTSERVICE_HOSTPORT = 2100; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 982ff200b3a..02a48d76de8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -68,6 +68,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.ORACLE_OPERATOR_NS; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.SKIP_BUILD_IMAGES_IF_EXISTS; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; @@ -107,6 +108,7 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.doesImageExist; import static oracle.weblogic.kubernetes.assertions.TestAssertions.imageExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DbUtils.installDBOperator; import static oracle.weblogic.kubernetes.utils.FileUtils.checkDirectory; import static oracle.weblogic.kubernetes.utils.FileUtils.cleanupDirectory; import static oracle.weblogic.kubernetes.utils.IstioUtils.installIstio; @@ -308,6 +310,8 @@ public void beforeAll(ExtensionContext context) { if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { installTraefikLB(); } + //install Oracle Database operator as a one time task + installOracleDBOperator(); // set initialization success to true, not counting the istio installation as not all tests use istio isInitializationSuccessful = true; @@ -363,6 +367,7 @@ public void close() { if (!OKD && !OKE_CLUSTER && !OCNE && !CRIO) { logger.info("Delete istio-system namespace after all test suites are run"); deleteNamespace("istio-system"); + deleteNamespace(ORACLE_OPERATOR_NS); } logger.info("Cleanup WIT/WDT binary form {0}", RESULTS_ROOT); try { @@ -662,6 +667,13 @@ private void installTraefikLB() { //expose traefik node port service and get route host //oc -n ns-abcdef expose service nginx-release-nginx-ingress-nginx-controller //oc -n ns-abcdef get routes nginx-release-nginx-ingress-nginx-controller '-o=jsonpath={.spec.host}' - } + } + + private void installOracleDBOperator() { + //install Oracle Database Operator + String namespace = ORACLE_OPERATOR_NS; + assertDoesNotThrow(() -> new Namespace().name(namespace).create()); + assertDoesNotThrow(() -> installDBOperator(), "Failed to install database operator"); + } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java index 5ccf76afdbc..de2613dd55d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java @@ -83,6 +83,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.ORACLE_DB_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.ORACLE_OPERATOR_NS; import static oracle.weblogic.kubernetes.TestConstants.ORACLE_RCU_SECRET_MOUNT_PATH; import static oracle.weblogic.kubernetes.TestConstants.ORACLE_RCU_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.ORACLE_RCU_SECRET_VOLUME; @@ -767,10 +768,11 @@ public static void updateRcuAccessSecret(String secretName, String namespace, /** * Install Oracle Database Operator. * - * @param namespace name of the namespace * @throws IOException when fails to modify operator yaml file */ - public static void installDBOperator(String namespace) throws IOException { + public static synchronized void installDBOperator() throws IOException { + String namespace = ORACLE_OPERATOR_NS; + String dbOpPodName = "oracle-database-operator-controller-manager"; Path operatorYamlSrcFile = Paths.get(RESOURCE_DIR, "dboperator", "oracle-database-operator.yaml"); Path operatorYamlDestFile = Paths.get(DOWNLOAD_DIR, namespace, "oracle-database-operator.yaml"); @@ -791,8 +793,6 @@ public static void installDBOperator(String namespace) throws IOException { boolean response = Command.withParams(params).execute(); assertTrue(response, "Failed to install Oracle database operator"); - String dbOpPodName = "oracle-database-operator-controller-manager"; - // wait for the pod to be ready getLogger().info("Wait for the database operator {0} pod to be ready in namespace {1}", dbOpPodName, namespace); From 0b6757b9249d812b9e9f52a98cbad49ba1890572 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 7 Jun 2024 10:26:13 -0400 Subject: [PATCH 079/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index e2040b1feca..a9cedfdcb32 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -24,7 +24,7 @@ org.apache.maven maven-plugin-api - 3.9.6 + 3.9.7 provided diff --git a/pom.xml b/pom.xml index 32f03bcb362..e73aaab7977 100644 --- a/pom.xml +++ b/pom.xml @@ -662,7 +662,7 @@ 21 21 [3.8.1,) - 3.4.1 + 3.5.0 3.3.2 3.13.0 3.1.2 @@ -677,7 +677,7 @@ 3.2.5 3.6.1 3.5.3 - 3.2.0 + 3.3.0 10.17.0 1.0 3.3.2 From a011aada009d89c6df64a26ea21d46c2d0c6dca9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 7 Jun 2024 14:03:31 -0400 Subject: [PATCH 080/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e73aaab7977..0f3638066f7 100644 --- a/pom.xml +++ b/pom.xml @@ -676,7 +676,7 @@ 3.6.3 3.2.5 3.6.1 - 3.5.3 + 3.6.0 3.3.0 10.17.0 1.0 From 2e4ed95ffc15352bf3b84cb3ec05ffc85b8d4966 Mon Sep 17 00:00:00 2001 From: jshum Date: Tue, 11 Jun 2024 04:03:46 -0500 Subject: [PATCH 081/356] back port liveness probe from main --- .../src/main/resources/scripts/livenessProbe.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/operator/src/main/resources/scripts/livenessProbe.sh b/operator/src/main/resources/scripts/livenessProbe.sh index c5c93e1a49b..efc0d6538c6 100755 --- a/operator/src/main/resources/scripts/livenessProbe.sh +++ b/operator/src/main/resources/scripts/livenessProbe.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2017, 2023, Oracle and/or its affiliates. +# Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Kubernetes periodically calls this liveness probe script to determine whether @@ -73,10 +73,18 @@ if [ "${MOCK_WLS}" != 'true' ]; then # Adjust PATH if necessary before calling jps adjustPath - if [ `jps -v | grep -c " -Dweblogic.Name=${SERVER_NAME} "` -eq 0 ]; then - trace SEVERE "WebLogic Server instance process not found." + if [ `jps -l | grep -c " weblogic.NodeManager"` -eq 0 ]; then + trace SEVERE "WebLogic NodeManager process not found." exit $RETVAL fi + + if [ ! -f ${STATEFILE} ] || [ `grep -c "SHUT" ${STATEFILE}` -eq 0 ]; then + # Only check for running server instance if the state is not SHUTDOWN or SHUTTING_DOWN + if [ `jps -v | grep -c " -Dweblogic.Name=${SERVER_NAME} "` -eq 0 ]; then + trace SEVERE "WebLogic Server instance process not found." + exit $RETVAL + fi + fi fi if [ -f ${STATEFILE} ] && [ `grep -c "FAILED" ${STATEFILE}` -eq 1 ]; then From 1e829bd2e11561c2d038065929cc18c995f9affb Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 11 Jun 2024 19:28:51 +0000 Subject: [PATCH 082/356] exclude db operator install in ocne and okd --- .../weblogic/kubernetes/extensions/InitializationTasks.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 02a48d76de8..fc3d0dd432b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -311,7 +311,9 @@ public void beforeAll(ExtensionContext context) { installTraefikLB(); } //install Oracle Database operator as a one time task - installOracleDBOperator(); + if (!OCNE && !OKD) { + installOracleDBOperator(); + } // set initialization success to true, not counting the istio installation as not all tests use istio isInitializationSuccessful = true; From edac29c138dd2daa67abf92b5376b8cdd1e5df99 Mon Sep 17 00:00:00 2001 From: jshum Date: Tue, 11 Jun 2024 14:51:18 -0500 Subject: [PATCH 083/356] Correct logic where one of the mountPath is exactly the same as logHomePath --- .../oracle/kubernetes/weblogic/domain/model/DomainResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java index 0de0124519c..4383203d326 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java @@ -1448,7 +1448,7 @@ private void addUnmappedLogHome() { } private boolean mapsLogHome(String mountPath) { - return getLogHome().startsWith(separatorTerminated(mountPath)); + return separatorTerminated(getLogHome()).startsWith(separatorTerminated(mountPath)); } private String separatorTerminated(String path) { From 052162c336479b3b13ba21b681f0626476ca9461 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Tue, 11 Jun 2024 22:53:50 +0000 Subject: [PATCH 084/356] backport owls118470 to support reporting error conditions for... --- .../common/logging/MessageKeys.java | 2 ++ common/src/main/resources/Operator.properties | 2 ++ .../operator/DomainProcessorImpl.java | 28 +++++++++++++++++ .../operator/DomainStatusUpdater.java | 23 ++++++++++++++ .../operator/KubernetesConstants.java | 3 +- .../operator/helpers/PodHelper.java | 22 ++++++++++++++ .../operator/DomainStatusUpdateTestBase.java | 30 +++++++++++++++++++ 7 files changed, 109 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/oracle/kubernetes/common/logging/MessageKeys.java b/common/src/main/java/oracle/kubernetes/common/logging/MessageKeys.java index 9f06eb4c4cc..60cf4c69656 100644 --- a/common/src/main/java/oracle/kubernetes/common/logging/MessageKeys.java +++ b/common/src/main/java/oracle/kubernetes/common/logging/MessageKeys.java @@ -233,6 +233,8 @@ public class MessageKeys { public static final String NO_MATCH_VOLUME_WITH_PVC = "WLSDO-0063"; public static final String NO_VOLUME_WITH_PVC = "WLSDO-0064"; public static final String WALLET_KEY_NOT_FOUND = "WLSDO-0065"; + public static final String POD_UNSCHEDULABLE = "WLSDO-0066"; + public static final String POD_UNSCHEDULABLE_MESSAGE = "WLSDO-0067"; // domain event messages public static final String DOMAIN_AVAILABLE_EVENT_PATTERN = "WLSEO-0001"; diff --git a/common/src/main/resources/Operator.properties b/common/src/main/resources/Operator.properties index 6d2f5dcc31c..7da870f3804 100644 --- a/common/src/main/resources/Operator.properties +++ b/common/src/main/resources/Operator.properties @@ -278,6 +278,8 @@ WLSDO-0064=When ''spec.configuration.initializeDomainOnPV'' is specified to auto but not the PersistentVolumeClaim (PVC), at least one of the volumes in ''spec.serverPod.volumes'' should contain a PVC. WLSDO-0065=The OPSS wallet password secret ''{0}'' is specified but the required ''walletPassword'' key is missing \ in the specified secret ''{1}''. +WLSDO-0066=Pod ''{0}'' is Unschedulable, reason: ''{1}'' +WLSDO-0067=One or more pods in the domain cannot be scheduled. Please check individual pod status for details. oneEnvVar=variable multipleEnvVars=variables diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java b/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java index 7bf80940436..3a6f8a8cdd6 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java @@ -25,7 +25,9 @@ import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimStatus; import io.kubernetes.client.openapi.models.V1Pod; +import io.kubernetes.client.openapi.models.V1PodCondition; import io.kubernetes.client.openapi.models.V1PodDisruptionBudget; +import io.kubernetes.client.openapi.models.V1PodStatus; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.util.Watch; import oracle.kubernetes.common.logging.LoggingFilter; @@ -67,9 +69,12 @@ import oracle.kubernetes.weblogic.domain.model.ServerStatus; import org.jetbrains.annotations.NotNull; +import static oracle.kubernetes.common.logging.MessageKeys.POD_UNSCHEDULABLE; import static oracle.kubernetes.common.logging.MessageKeys.PVC_NOT_BOUND_ERROR; import static oracle.kubernetes.operator.DomainStatusUpdater.createInternalFailureSteps; import static oracle.kubernetes.operator.DomainStatusUpdater.createIntrospectionFailureSteps; +import static oracle.kubernetes.operator.KubernetesConstants.POD_SCHEDULED; +import static oracle.kubernetes.operator.KubernetesConstants.UNSCHEDULABLE_REASON; import static oracle.kubernetes.operator.ProcessingConstants.SERVER_HEALTH_MAP; import static oracle.kubernetes.operator.ProcessingConstants.SERVER_STATE_MAP; import static oracle.kubernetes.operator.helpers.EventHelper.EventItem.CLUSTER_CHANGED; @@ -604,6 +609,10 @@ private void processServerPodWatch(V1Pod pod, String watchType) { LOGGER.info(MessageKeys.POD_EVICTED_NO_RESTART, getPodName(pod), getPodStatusMessage(pod)); } } + boolean isUnschedulable = PodHelper.hasUnSchedulableCondition(pod); + if (isUnschedulable) { + LOGGER.info(POD_UNSCHEDULABLE, getPodName(pod), getUnSchedulableConditionMessage(pod)); + } break; case DELETED: boolean removed = info.deleteServerPodFromEvent(serverName, pod); @@ -618,6 +627,25 @@ private void processServerPodWatch(V1Pod pod, String watchType) { } } + /** + * If a pod is unschedulable, return the condition's message. + * @param pod Kubernetes V1Pod + * @return message for the unschedulable pod condition + */ + public static String getUnSchedulableConditionMessage(V1Pod pod) { + return Optional.ofNullable(pod) + .filter(PodHelper::isPending) + .map(V1Pod::getStatus) + .map(V1PodStatus::getConditions) + .orElse(Collections.emptyList()) + .stream() + .filter(condition -> POD_SCHEDULED.equals(condition.getType()) + && UNSCHEDULABLE_REASON.equals(condition.getReason())) + .map(V1PodCondition::getMessage) + .findFirst().orElse(null); + } + + private String getPodLabel(V1Pod pod, String labelName) { return Optional.ofNullable(pod) .map(V1Pod::getMetadata) diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java b/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java index feb053c2cb0..97c93e84aa5 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java @@ -76,6 +76,7 @@ import static oracle.kubernetes.common.logging.MessageKeys.PODS_FAILED; import static oracle.kubernetes.common.logging.MessageKeys.PODS_NOT_READY; import static oracle.kubernetes.common.logging.MessageKeys.PODS_NOT_RUNNING; +import static oracle.kubernetes.common.logging.MessageKeys.POD_UNSCHEDULABLE_MESSAGE; import static oracle.kubernetes.operator.ClusterResourceStatusUpdater.createClusterResourceStatusUpdaterStep; import static oracle.kubernetes.operator.KubernetesConstants.HTTP_NOT_FOUND; import static oracle.kubernetes.operator.KubernetesConstants.MINIMUM_CLUSTER_COUNT; @@ -751,6 +752,11 @@ private void setStatusConditions(DomainStatus status) { if (isHasFailedPod()) { addFailure(status, new DomainCondition(FAILED).withReason(SERVER_POD) .withFailureInfo(getDomain().getSpec()).withMessage(getPodFailedMessage())); + } else if (isPodUnSchedulable()) { + if (!alreadyReportedPodUnSchedulableCondition(status)) { + addFailure(status, new DomainCondition(FAILED).withReason(SERVER_POD) + .withFailureInfo(getDomain().getSpec()).withMessage(getPodUnSchedulableMessage())); + } } else if (hasPodNotRunningInTime()) { addFailure(status, new DomainCondition(FAILED).withReason(SERVER_POD) .withFailureInfo(getDomain().getSpec()).withMessage(getPodNotRunningMessage())); @@ -777,6 +783,10 @@ private String getPodNotReadyMessage() { return LOGGER.formatMessage(PODS_NOT_READY); } + private String getPodUnSchedulableMessage() { + return LOGGER.formatMessage(POD_UNSCHEDULABLE_MESSAGE); + } + private String getPodFailedMessage() { return LOGGER.formatMessage(PODS_FAILED); } @@ -1258,6 +1268,11 @@ private boolean stillHasPodPendingRestart(DomainStatus status) { .anyMatch(m -> m.containsKey(LabelConstants.MII_UPDATED_RESTART_REQUIRED_LABEL)); } + private boolean alreadyReportedPodUnSchedulableCondition(DomainStatus status) { + return status.getConditions().stream().anyMatch( + condition -> getPodUnSchedulableMessage().equals(condition.getMessage())); + } + private V1Pod getServerPod(ServerStatus serverStatus) { return getInfo().getServerPod(serverStatus.getServerName()); } @@ -1325,6 +1340,14 @@ private boolean hasPodNotRunningInTime() { return getInfo().getServerPods().anyMatch(this::isNotRunningInTime); } + private boolean isPodUnSchedulable() { + return getInfo().getServerPods().anyMatch(this::isPodUnSchedulable); + } + + private boolean isPodUnSchedulable(V1Pod pod) { + return PodHelper.isPending(pod) && PodHelper.hasUnSchedulableCondition(pod); + } + private boolean isNotRunningInTime(V1Pod pod) { return PodHelper.isPending(pod) && hasBeenPendingExceededWaitTime(pod); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java index 27778fce34e..f5b72cb2d0a 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java @@ -56,7 +56,8 @@ public interface KubernetesConstants { String NAMESPACE = "Namespace"; String POD = "Pod"; String EVICTED_REASON = "Evicted"; - + String UNSCHEDULABLE_REASON = "Unschedulable"; + String POD_SCHEDULED = "PodScheduled"; int DEFAULT_EXPORTER_SIDECAR_PORT = 8080; //---------- HTTP statuses returned from Kubernetes ---------- diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java index 125268eede3..5f16b93a943 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java @@ -49,6 +49,8 @@ import oracle.kubernetes.weblogic.domain.model.Shutdown; import static oracle.kubernetes.operator.KubernetesConstants.EVICTED_REASON; +import static oracle.kubernetes.operator.KubernetesConstants.POD_SCHEDULED; +import static oracle.kubernetes.operator.KubernetesConstants.UNSCHEDULABLE_REASON; import static oracle.kubernetes.operator.LabelConstants.CLUSTERNAME_LABEL; import static oracle.kubernetes.operator.LabelConstants.SERVERNAME_LABEL; import static oracle.kubernetes.operator.ProcessingConstants.SERVERS_TO_ROLL; @@ -214,6 +216,11 @@ private static boolean isReadyCondition(V1PodCondition condition) { return "Ready".equals(condition.getType()) && "True".equals(condition.getStatus()); } + private static boolean isUnSchedulableTheReason(V1PodCondition condition) { + return POD_SCHEDULED.equals(condition.getType()) && "False".equals(condition.getStatus()) + && UNSCHEDULABLE_REASON.equals(condition.getReason()); + } + private static boolean isReadyNotTrueCondition(V1PodCondition condition) { return "Ready".equals(condition.getType()) && !"True".equals(condition.getStatus()); } @@ -299,6 +306,21 @@ public static boolean shouldRestartEvictedPod(V1Pod pod) { return isEvicted(pod) && TuningParameters.getInstance().isRestartEvictedPods(); } + /** + * Return true if the pod has unschedulable condition. + * @param pod Kubernetes Pod + * @return true if the pod is unschedulable + */ + public static boolean hasUnSchedulableCondition(V1Pod pod) { + return Optional.ofNullable(pod) + .filter(PodHelper::isPending) + .map(V1Pod::getStatus) + .map(V1PodStatus::getConditions) + .orElse(Collections.emptyList()) + .stream() + .anyMatch(PodHelper::isUnSchedulableTheReason); + } + /** * Returns the domain UID associated with the specified pod. * @param pod the pod diff --git a/operator/src/test/java/oracle/kubernetes/operator/DomainStatusUpdateTestBase.java b/operator/src/test/java/oracle/kubernetes/operator/DomainStatusUpdateTestBase.java index b68bd75179e..0195e6604a2 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DomainStatusUpdateTestBase.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DomainStatusUpdateTestBase.java @@ -239,6 +239,22 @@ private V1Pod getPod(String serverName) { return info.getServerPod(serverName); } + private void setPodUnschedulable(String serverName) { + V1Pod pod = getPod(serverName); + V1PodStatus status = pod.getStatus(); + if (status == null) { + status = new V1PodStatus(); + } + pod.setStatus(status + .startTime(SystemClock.now()) + .phase("Pending") + .addConditionsItem(new V1PodCondition().type("PodScheduled").status("False").reason("Unschedulable") + .message("0/1 nodes are available: 1 node(s) didn't match pod topology" + + " spread constraints (missing required label). preemption: 0/1 nodes are available: " + + "1 Preemption is not helpful for scheduling..")) + ); + } + @Test void statusStep_usesServerFromWlsConfig() { defineScenario() @@ -1033,6 +1049,20 @@ void whenPodPendingWithinTimeLimit_removePreviousServerPodFailures() { assertThat(getRecordedDomain(), not(hasCondition(FAILED))); } + @Test + void whenPodIsUnschedulable_reportServerPodFailure() { + defineScenario() + .withServerState("server1", new V1ContainerStateWaiting().reason(null)) + .build(); + setPodUnschedulable("server1"); + + SystemClockTestSupport.increment(21); + updateDomainStatus(); + assertThat(getRecordedDomain(), hasCondition(FAILED).withReason(SERVER_POD) + .withMessageContaining("One or more pods in the domain cannot be scheduled.")); + } + + // todo remove server pod failures when OK @Test From a7dba659abc1499206281078073feac6ee5ecf10 Mon Sep 17 00:00:00 2001 From: xiancao Date: Thu, 13 Jun 2024 01:46:23 +0000 Subject: [PATCH 085/356] disable installOracleDBOperator in crio pipeline --- .../weblogic/kubernetes/extensions/InitializationTasks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index fc3d0dd432b..4ae6b74af59 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -311,7 +311,7 @@ public void beforeAll(ExtensionContext context) { installTraefikLB(); } //install Oracle Database operator as a one time task - if (!OCNE && !OKD) { + if (!OCNE && !OKD && !CRIO) { installOracleDBOperator(); } From 6526cd36afa322e7c8328e31167ee9adc509e090 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 17 Jun 2024 17:45:22 +0000 Subject: [PATCH 086/356] Back port changes to 4.2 for using terraform module OKE creation --- Jenkinsfile.oke | 27 +-- .../resources/oke/terraform/okemodule/c1.tf | 202 ++++++++++++++++++ .../oke/terraform/okemodule/export.tf | 16 ++ .../oke/terraform/okemodule/export_set.tf | 10 + .../oke/terraform/okemodule/file_system.tf | 18 ++ .../oke/terraform/okemodule/locals.tf | 122 +++++++++++ .../oke/terraform/okemodule/oke.create.sh | 197 +++++++++++++++++ .../oke/terraform/okemodule/oke.delete.sh | 82 +++++++ .../oke/terraform/okemodule/providers.tf | 24 +++ .../terraform/okemodule/scripts/cloud-init.sh | 23 ++ .../scripts/generate_kubeconfig.template.sh | 5 + .../kubeconfig_set_credentials.template.sh | 12 ++ .../okemodule/scripts/set_alias.template.sh | 5 + .../scripts/token_helper.template.sh | 15 ++ .../oke/terraform/okemodule/template.tfvars | 56 +++++ .../oke/terraform/okemodule/templates.tf | 44 ++++ .../oke/terraform/okemodule/variables.tf | 186 ++++++++++++++++ .../oke/terraform/okemodule/versions.tf | 12 ++ 18 files changed, 1040 insertions(+), 16 deletions(-) create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/c1.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/export.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/export_set.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/file_system.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/locals.tf create mode 100755 integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/providers.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/scripts/cloud-init.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/scripts/generate_kubeconfig.template.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/scripts/kubeconfig_set_credentials.template.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/scripts/set_alias.template.sh create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/scripts/token_helper.template.sh create mode 100755 integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/templates.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/variables.tf create mode 100644 integration-tests/src/test/resources/oke/terraform/okemodule/versions.tf diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index ce27d402e33..1d195c5d0b6 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -63,7 +63,8 @@ pipeline { wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" kubeconfig_file = "${WORKSPACE}/terraform/${CLUSTER_NAME}_kubeconfig" availability_domain = "${env.JOB_NAME == 'wko-oke-nightly-parallel' ? 'mFEn:PHX-AD-1' : 'mFEn:PHX-AD-1'}" - oke_run = "${env.JOB_NAME == 'wko-oke-nightly' ? 'okeint' : 'okeintdev'}" + //oke_run = "${env.JOB_NAME == 'wko-oke-nightly' ? 'okeint' : 'okeintdev'}" + oke_run = "okemodule" } @@ -336,6 +337,7 @@ pipeline { #export IMAGE_TAG_WEBLOGIC="12.2.1.4" #export IMAGE_TAG_FMWINFRA="12.2.1.4" ssh_pubkey=`cat ${wkotest_ssh_pubcert}` + ssh_pk=`cat ${wkotest_ssh_pk}` ################# echo "Generating property file oci.prop for terraform scripts" @@ -362,13 +364,15 @@ mounttarget.ocid=${MOUNT_TARGET_OCID} nodepool.imagename=${IMAGE_ID} k8s.version=v${OKE_KUBE_VERSION} nodepool.ssh.pubkey=${ssh_pubkey} +nodepool.ssh.pk=${ssh_pk} +nodepool.ssh.pubkeypath=${wkotest_ssh_pubcert} +nodepool.ssh.pkpath=${wkotest_ssh_pk} terraform.installdir=${WORKSPACE}/terraform/terraforminstall EOF ################## echo "prop files " cat $OCI_PROP_FILE - cp -rf ${WORKSPACE}/kubernetes/samples/scripts/terraform/template.tfvars ${WORKSPACE}/terraform/. mkdir -p ${WORKSPACE}/terraform/terraforminstall ''' @@ -415,7 +419,7 @@ EOF echo 'Create a OKE cluster ${CLUSTER_NAME}' - cp -rf ${terraform_script_dir_name}/*.* ${WORKSPACE}/terraform/. + cp -rf ${terraform_script_dir_name}/* ${WORKSPACE}/terraform/. chmod 777 ${WORKSPACE}/terraform/*.sh mkdir -p ${WORKSPACE}/terraform/terraforminstall @@ -462,12 +466,7 @@ EOF compartment_ocid=${compartment_id} echo "creating storage class to setup OFSS ..." echo "getting MountTarget ID" - if [ "${JOB_NAME}" = "wko-oke-nightly" ]; then - mount_target_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | .id'` - else - mount_target_id=${MOUNT_TARGET_OCID} - fi - + mount_target_id=${MOUNT_TARGET_OCID} clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') @@ -493,7 +492,7 @@ EOF environment { runtime_path = "${WORKSPACE}/bin:${PATH}" clusterName = "${CLUSTER_NAME}" - FSS_DIR = "${env.JOB_NAME == 'wko-oke-nightly' ? '/oketest1,/oketest2,/oketest3,/oketest4,/oketest5,/oketest6,/oketest7,/oketest8,/oketest9,/oketest10,/oketest11,/oketest12,/oketest13,/oketest14,/oketest15' : '/${clusterName}oketest1,/${clusterName}oketest2'}" + FSS_DIR = "${env.JOB_NAME == 'wko-oke-nightly' ? '/${clusterName}oketest1,/${clusterName}oketest2' : '/${clusterName}oketest1,/${clusterName}oketest2'}" } @@ -549,13 +548,9 @@ EOF NODE_IP=`kubectl get nodes -o wide| awk '{print $7}'| head -n2 | tail -n1` echo "second node external IP ${NODE_IP}" export NODE_IP=${NODE_IP} - if [ "${JOB_NAME}" = "wko-oke-nightly" ]; then - echo "Mount Target setup for wko-oke-nightly" - mt_privateip_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | ."private-ip-ids"[]'` - else - mt_privateip_id=`oci fs mount-target get --mount-target-id=${MOUNT_TARGET_OCID} | jq -r '.data| ."private-ip-ids"[]'` - fi + mt_privateip_id=`oci fs mount-target get --mount-target-id=${MOUNT_TARGET_OCID} | jq -r '.data| ."private-ip-ids"[]'` + # Check if the mt_privateip_id is an array if [ "$(declare -p mt_privateip_id 2>/dev/null | grep -o 'declare -a')" == "declare -a" ]; then # Select first diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/c1.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/c1.tf new file mode 100644 index 00000000000..ec0cbc6f86e --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/c1.tf @@ -0,0 +1,202 @@ +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + + +data "oci_containerengine_cluster_kube_config" "public" { + count = local.cluster_enabled && local.public_endpoint_available ? 1 : 0 + + cluster_id = local.cluster_id + endpoint = "PUBLIC_ENDPOINT" +} + + +data "oci_containerengine_cluster_kube_config" "private" { + count = local.cluster_enabled && local.private_endpoint_available ? 1 : 0 + + cluster_id = local.cluster_id + endpoint = "PRIVATE_ENDPOINT" +} + +data "oci_containerengine_clusters" "existing_cluster" { + count = var.cluster_id != null ? 1 : 0 + compartment_id = var.compartment_id + + state = ["ACTIVE","UPDATING"] + filter { + name = "id" + values = [var.cluster_id] + } +} + +# Obtain cluster Kubeconfig. +data "oci_containerengine_cluster_kube_config" "kube_config" { + #cluster_id = oci_containerengine_cluster.k8s_cluster.id + cluster_id = one(module.c1[*].cluster_id) +} + + +output "cluster_id" { + description = "ID of the OKE cluster" + value = one(module.c1[*].cluster_id) +} + + +output "cluster_kubeconfig" { + description = "OKE kubeconfig" + value = var.output_detail ? ( + local.public_endpoint_available ? local.kubeconfig_public : local.kubeconfig_private + ) : null +} + +variable "cluster_kube_config_expiration" { + default = 2592000 +} + +variable "cluster_kube_config_token_version" { + default = "2.0.0" +} + +output "cluster_ca_cert" { + description = "OKE cluster CA certificate" + value = var.output_detail && length(local.cluster_ca_cert) > 0 ? local.cluster_ca_cert : null +} + +output "apiserver_private_host" { + description = "Private OKE cluster endpoint address" + value = local.apiserver_private_host +} + + +locals { + cluster_enabled = var.create_cluster || coalesce(var.cluster_id, "none") != "none" + cluster_id = var.create_cluster ? one(module.c1[*].cluster_id) : var.cluster_id + cluster_name = var.cluster_name + + cluster-context = try(format("context-%s", substr(local.cluster_id, -11, -1)), "") + + existing_cluster_endpoints = coalesce(one(flatten(data.oci_containerengine_clusters.existing_cluster[*].clusters[*].endpoints)), tomap({})) + public_endpoint_available = var.cluster_id != null ? length(lookup(local.existing_cluster_endpoints, "public_endpoint", "")) > 0 : var.control_plane_is_public && var.assign_public_ip_to_control_plane + private_endpoint_available = var.cluster_id != null ? length(lookup(local.existing_cluster_endpoints, "private_endpoint", "")) > 0 : true + kubeconfig_public = var.control_plane_is_public ? try(yamldecode(replace(lookup(one(data.oci_containerengine_cluster_kube_config.public), "content", ""), local.cluster-context, var.cluster_name)), tomap({})) : null + kubeconfig_private = try(yamldecode(replace(lookup(one(data.oci_containerengine_cluster_kube_config.private), "content", ""), local.cluster-context, var.cluster_name)), tomap({})) + + kubeconfig_clusters = try(lookup(local.kubeconfig_private, "clusters", []), []) + apiserver_private_host = (var.create_cluster + ? try(split(":", one(module.c1[*].endpoints.private_endpoint))[0], "") + : split(":", replace(try(lookup(lookup(local.kubeconfig_clusters[0], "cluster", {}), "server", ""), "none"), "https://", ""))[0]) + + kubeconfig_ca_cert = try(lookup(lookup(local.kubeconfig_clusters[0], "cluster", {}), "certificate-authority-data", ""), "none") + cluster_ca_cert = coalesce(var.cluster_ca_cert, local.kubeconfig_ca_cert) +} + + +module "c1" { + + source = "oracle-terraform-modules/oke/oci" + version = "5.1.1" + + count = lookup(lookup(var.clusters, "c1"), "enabled") ? 1 : 0 + + home_region = lookup(local.regions, var.home_region) + + #region = lookup(local.regions, lookup(lookup(var.clusters, "c1"), "region")) + region = lookup(local.regions, var.home_region) + + tenancy_id = var.tenancy_id + + # general oci parameters + compartment_id = var.compartment_id + + # ssh keys + ssh_private_key_path = var.ssh_private_key_path + ssh_public_key_path = var.ssh_public_key_path + +# Network + create_vcn = false + vcn_id = var.vcn_id + + + # networking + create_drg = var.oke_control_plane == "private" ? true : false + drg_display_name = var.cluster_name + + + vcn_cidrs = [lookup(lookup(var.clusters, "c1"), "vcn")] + vcn_name = "VCN-wktiso1" + + #subnets + subnets = { + pub_lb = { id = var.pub_lb_id } + operator = { id = var.pub_lb_id } + bastion = { id = var.pub_lb_id } + workers = { id = var.worker_subnet_id } + cp = {id = var.control_plane_subnet_id } + } + + # bastion host + create_bastion = true # *true/false + bastion_allowed_cidrs = [] # e.g. ["0.0.0.0/0"] to allow traffic from all sources + bastion_availability_domain = null # Defaults to first available + bastion_image_id = null # Ignored when + bastion_image_os = "Oracle Linux" # Ignored when bastion_image_type = "custom" + bastion_image_os_version = "8" # Ignored when bastion_image_type = "custom" + bastion_image_type = "platform" # platform/custom + bastion_nsg_ids = [] # Combined with created NSG when enabled in var.nsgs + bastion_public_ip = null # Ignored when create_bastion = true + #bastion_type = "public" # *public/private + bastion_upgrade = false # true/*false + bastion_user = "opc" + + bastion_shape = {shape = var.node_shape,ocpus = 1,memory = 4,boot_volume_size = 50} + #create_bastion = true + allow_bastion_cluster_access = true + bastion_is_public = true + #bastion_allowed_cidrs = ["0.0.0.0/0"] + #bastion_upgrade = false + + # operator host + create_operator = false + operator_upgrade = false + operator_install_helm = true + #operator_install_kubectl_from_repo = true + operator_cloud_init = [] + create_iam_resources = false + create_iam_operator_policy = "never" + operator_install_k9s = false + + # oke cluster options + cluster_name = var.cluster_name + cluster_type = var.cluster_type + cni_type = var.preferred_cni + control_plane_is_public = true + control_plane_allowed_cidrs = [local.anywhere] + kubernetes_version = var.kubernetes_version + services_cidr = lookup(lookup(var.clusters, "c1"), "services") + + + # node pools + allow_worker_ssh_access = true + kubeproxy_mode = "iptables" + worker_pool_mode = "node-pool" + worker_pools = var.nodepools + worker_cloud_init = local.worker_cloud_init + worker_image_type = "oke" + + # oke load balancers + load_balancers = "both" + preferred_load_balancer = "public" + + + + user_id = var.user_id + providers = { + oci = oci.c1 + oci.home = oci.home + } +} + + +resource "local_file" "test_kube_config_file" { + content = data.oci_containerengine_cluster_kube_config.kube_config.content + filename = "${path.module}/${var.cluster_name}_kubeconfig" +} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/export.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/export.tf new file mode 100644 index 00000000000..c173cbbb8f5 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/export.tf @@ -0,0 +1,16 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ +resource "oci_file_storage_export" "oketest_export1" { + #Required + export_set_id = oci_file_storage_export_set.oketest_export_set.id + file_system_id = oci_file_storage_file_system.oketest_fs1.id + path = "/${var.cluster_name}oketest1" +} +resource "oci_file_storage_export" "oketest_export2" { + #Required + export_set_id = oci_file_storage_export_set.oketest_export_set.id + file_system_id = oci_file_storage_file_system.oketest_fs2.id + path = "/${var.cluster_name}oketest2" +} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/export_set.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/export_set.tf new file mode 100644 index 00000000000..b42df2423ad --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/export_set.tf @@ -0,0 +1,10 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +resource "oci_file_storage_export_set" "oketest_export_set" { + # Required + mount_target_id = var.mount_target_ocid + # mount_target_id = oci_file_storage_mount_target.oketest_mount_target.id +} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/file_system.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/file_system.tf new file mode 100644 index 00000000000..79121852608 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/file_system.tf @@ -0,0 +1,18 @@ +/* +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + + +resource "oci_file_storage_file_system" "oketest_fs1" { + #Required + availability_domain = var.availability_domain + + #availability_domain = data.oci_identity_availability_domain.ad1.name + compartment_id = var.compartment_id +} +resource "oci_file_storage_file_system" "oketest_fs2" { + #Required + availability_domain = var.availability_domain + compartment_id = var.compartment_id +} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/locals.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/locals.tf new file mode 100644 index 00000000000..3bbe00b8a4d --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/locals.tf @@ -0,0 +1,122 @@ +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +locals { + + all_ports = -1 + + # Protocols + # See https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml + all_protocols = "all" + icmp_protocol = 1 + tcp_protocol = 6 + udp_protocol = 17 + + anywhere = "0.0.0.0/0" + rule_type_nsg = "NETWORK_SECURITY_GROUP" + rule_type_cidr = "CIDR_BLOCK" + rule_type_service = "SERVICE_CIDR_BLOCK" + + bastion_ip = one(element([module.c1[*].bastion_public_ip], 0)) + + operator_ip = one(element([module.c1[*].operator_private_ip], 0)) + operator_install_kubectl_from_repo = true + #control_plane_subnet_id = "ocid1.subnet.oc1.phx.aaaaaaaay235gkzcszhasbraiy34e3g6kc4iyk47yzon3u5qjvhewaasq2za" + # TODO: check when is 15021 required for public + public_lb_allowed_ports = [80, 443, 15021] + + # ports required to be opened for inter-cluster communication between for Istio + service_mesh_ports = [15012, 15017, 15021, 15443] + + regions = { + # Africa + johannesburg = "af-johannesburg-1" + + # Asia + chuncheon = "ap-chuncheon-1" + hyderabad = "ap-hyderabad-1" + mumbai = "ap-mumbai-1" + osaka = "ap-osaka-1" + seoul = "ap-seoul-1" + singapore = "ap-singapore-1" + tokyo = "ap-tokyo-1" + + # Europe + amsterdam = "eu-amsterdam-1" + frankfurt = "eu-frankfurt-1" + london = "uk-london-1" + madrid = "eu-madrid-1" + marseille = "eu-marseille-1" + milan = "eu-milan-1" + newport = "uk-cardiff-1" + paris = "eu-paris-1" + stockholm = "eu-stockholm-1" + zurich = "eu-zurich-1" + + # Middle East + abudhabi = "me-abudhabi-1" + dubai = "me-dubai-1" + jeddah = "me-jeddah-1" + jerusalem = "il-jerusalem-1" + + # Oceania + melbourne = "ap-melbourne-1" + sydney = "ap-sydney-1" + + + # South America + bogota = "sa-bogota-1" + santiago = "sa-santiago-1" + saupaulo = "sa-saupaulo-1" + valparaiso = "sa-valparaiso-1" + vinhedo = "sa-vinhedo-1" + + # North America + ashburn = "us-ashburn-1" + chicago = "us-chicago-1" + monterrey = "mx-monterrey-1" + montreal = "ca-montreal-1" + phoenix = "us-phoenix-1" + queretaro = "mx-queretaro-1" + sanjose = "us-sanjose-1" + toronto = "ca-toronto-1" + + # US Gov FedRamp + us-gov-ashburn = "us-langley-1" + us-gov-phoenix = "us-luke-1" + + # US Gov DISA L5 + us-dod-east = "us-gov-ashburn-1" + us-dod-north = "us-gov-chicago-1" + us-dod-west = "us-gov-phoenix-1" + + # UK Gov + uk-gov-south = "uk-gov-london-1" + uk-gov-west = "uk-gov-cardiff-1" + + # Australia Gov + au-gov-cbr = "ap-dcc-canberra-1" + + } + + worker_cloud_init = [ + { + content = <<-EOT + runcmd: + - 'echo "Kernel module configuration for Istio and worker node initialization"' + - 'modprobe br_netfilter' + - 'modprobe nf_nat' + - 'modprobe xt_REDIRECT' + - 'modprobe xt_owner' + - 'modprobe iptable_nat' + - 'modprobe iptable_mangle' + - 'modprobe iptable_filter' + - '/usr/libexec/oci-growfs -y' + - 'timedatectl set-timezone Australia/Sydney' + - 'curl --fail -H "Authorization: Bearer Oracle" -L0 http://169.254.169.254/opc/v2/instance/metadata/oke_init_script | base64 --decode >/var/run/oke-init.sh' + - 'bash -x /var/run/oke-init.sh' + EOT + content_type = "text/cloud-config", + } + ] +} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh new file mode 100755 index 00000000000..9648d5f74f5 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh @@ -0,0 +1,197 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +prop() { + grep "${1}" ${propsFile}| grep -v "#" | cut -d'=' -f2 +} + + +generateTFVarFile() { + tfVarsFiletfVarsFile=${terraformVarDir}/${clusterTFVarsFile}.tfvars + rm -f ${tfVarsFiletfVarsFile} + cp ${terraformVarDir}/template.tfvars $tfVarsFiletfVarsFile + chmod 777 ${terraformVarDir}/template.tfvars $tfVarsFiletfVarsFile + sed -i -e "s:@TENANCYOCID@:${tenancy_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@USEROCID@:${user_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@COMPOCID@:${compartment_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@SUBCOMPARTMENTOCID@:${sub_compartment_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@COMPARTMENTNAME@:${compartment_name}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@OKECLUSTERNAME@:${okeclustername}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s/@OCIAPIPUBKEYFINGERPRINT@/"${ociapi_pubkey_fingerprint}"/g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@OCIPRIVATEKEYPATH@:${ocipk_path}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@VCNCIDRPREFIX@:${vcn_cidr_prefix}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@VCNOCID@:${vcn_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@PUBSUBNETOCID@:${pub_subnet_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@PRIVATESUBNETOCID@:${private_subnet_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@VCNCIDR@:${vcn_cidr_prefix}.0.0/16:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@OKEK8SVERSION@:${k8s_version}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLSHAPE@:${nodepool_shape}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLIMAGENAME@:${nodepool_imagename}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLSSHPUBKEY@:${nodepool_ssh_pubkeypath}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@NODEPOOLSSHPK@:${nodepool_ssh_pkpath}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@REGION@:${region}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@MOUNTTARGETOCID@:${mount_target_ocid}:g" ${tfVarsFiletfVarsFile} + echo "Generated TFVars file [${tfVarsFiletfVarsFile}]" +} + +setupTerraform() { + mkdir ${terraformDir} + cd ${terraformDir} + if [[ "${OSTYPE}" == "darwin"* ]]; then + os_type="darwin" + elif [[ "${OSTYPE}" == "linux"* ]]; then + os_type="linux" + else + echo "Unsupported OS" + fi + curl -O https://releases.hashicorp.com/terraform/1.8.1/terraform_1.8.1_${os_type}_${platform}64.zip + unzip terraform_1.8.1_${os_type}_${platform}64.zip + chmod +x ${terraformDir}/terraform + export PATH=${terraformDir}:${PATH} +} + +deleteOlderVersionTerraformOCIProvider() { + if [ -d ~/.terraform.d/plugins ]; then + echo "Deleting older version of terraform plugins dir" + rm -rf ~/.terraform.d/plugins + fi + if [ -d ${terraformVarDir}/.terraform ]; then + rm -rf ${terraformVarDir}/.terraform + fi + if [ -e ~/.terraformrc ]; then + rm ~/.terraformrc + fi +} + +createCluster () { + cd ${terraformVarDir} + echo "terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + terraform plan -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + terraform apply -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars +} + +createRoleBindings () { + ${KUBERNETES_CLI:-kubectl} -n kube-system create serviceaccount $okeclustername-sa + ${KUBERNETES_CLI:-kubectl} create clusterrolebinding add-on-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:$okeclustername-sa + TOKENNAME=`${KUBERNETES_CLI:-kubectl} -n kube-system get serviceaccount/$okeclustername-sa -o jsonpath='{.secrets[0].name}'` + TOKEN=`${KUBERNETES_CLI:-kubectl} -n kube-system get secret $TOKENNAME -o jsonpath='{.data.token}'| base64 --decode` + ${KUBERNETES_CLI:-kubectl} config set-credentials $okeclustername-sa --token=$TOKEN + ${KUBERNETES_CLI:-kubectl} config set-context --current --user=$okeclustername-sa +} + +checkClusterRunning () { + + echo "Confirm we have ${KUBERNETES_CLI:-kubectl} working..." + myline=`${KUBERNETES_CLI:-kubectl} get nodes | awk '{print $2}'| tail -n+2` + status="NotReady" + max=50 + count=1 + + privateIP=${vcn_cidr_prefix//./\\.}\\.10\\. + privateIP=${vcn_cidr_prefix} + declare -a myline + myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) + NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` + status=$myline[0] + max=100 + count=1 + + for i in {0..1} + do + while [ "${myline[i]}" != "Ready" -a $count -le $max ] ; do + echo "echo '[ERROR] Some Nodes in the Cluster are not in the Ready Status , sleep 10s more ..." + sleep 10 + myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) + NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` + echo "myline[i] ${myline[i]}" + [[ ${myline[i]} -eq "Ready" ]] + echo "Status is ${myline[i]} Iter [$count/$max]" + count=`expr $count + 1` + done + done + + NODES=`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | wc -l` + if [ "$NODES" == "2" ]; then + echo '- looks good' + else + echo '- could not talk to cluster, aborting' + cd ${terraformVarDir} + terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + exit 1 + fi + + if [ $count -gt $max ] ; then + echo "[ERROR] Unable to start the nodes in oke cluster after 200s "; + cd ${terraformVarDir} + terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + exit 1 + fi +} + +#MAIN +propsFile=${1:-$PWD/oci.props} +terraformVarDir=${2:-$PWD} +platform=${3:-amd} + +#grep props's values from oci.props file + +clusterTFVarsFile=$(prop 'tfvars.filename') +tenancy_ocid=$(prop 'tenancy.ocid') +user_ocid=$(prop 'user.ocid') +compartment_ocid=$(prop 'compartment.ocid') +sub_compartment_ocid=$(prop 'sub.comp.ocid') +compartment_name=$(prop 'compartment.name') +okeclustername=$(prop 'okeclustername') +ociapi_pubkey_fingerprint=$(prop 'ociapi.pubkey.fingerprint') +ocipk_path=$(prop 'ocipk.path') +vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') +vcn_ocid=$(prop 'vcn.ocid') +pub_subnet_ocid=$(prop 'pub.subnet.ocid') +private_subnet_ocid=$(prop 'private.subnet.ocid') +k8s_version=$(prop 'k8s.version') +nodepool_shape=$(prop 'nodepool.shape') +nodepool_imagename=$(prop 'nodepool.imagename') +nodepool_ssh_pubkeypath=$(prop 'nodepool.ssh.pubkeypath') +nodepool_ssh_pkpath=$(prop 'nodepool.ssh.pkpath') +region=$(prop 'region') +terraformDir=$(prop 'terraform.installdir') +mount_target_ocid=$(prop 'mounttarget.ocid') + +# generate terraform configuration file with name $(clusterTFVarsFile).tfvar +#generateTFVarFile +generateTFVarFile + +# cleanup previously installed terraform binaries +rm -rf ${terraformDir} + +# download terraform binaries into ${terraformDir} +setupTerraform + +# clean previous versions of terraform oci provider +deleteOlderVersionTerraformOCIProvider + +chmod 600 ${ocipk_path} + +# run terraform init,plan,apply to create OKE cluster based on the provided tfvar file ${clusterTFVarsFile).tfvar +createCluster +#check status of OKE cluster nodes, destroy if can not access them +export KUBECONFIG=${terraformVarDir}/${okeclustername}_kubeconfig + + +export okeclustername=\"${okeclustername}\" + + + echo " oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"'" + +clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') +echo "clusterIp : $clusterIP" +clusterPublicIP=${clusterIP:1:-6} +echo " clusterPublicIP : ${clusterPublicIP}" +export NO_PROXY=$NO_PROXY,${clusterPublicIP} +echo "NO_PROXY:" $NO_PROXY + + +checkClusterRunning +echo "$okeclustername is up and running}" diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh new file mode 100644 index 00000000000..9648d87b6a8 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# +# This script deletes provisioned OKE Kubernetes cluster using terraform (https://www.terraform.io/) +# + +set -o errexit +set -o pipefail + +prop() { + grep "${1}" ${oci_property_file}| grep -v "#" | cut -d'=' -f2 +} + +cleanupLB() { + echo 'Clean up left over LB' + myvcn_id=`oci network vcn list --compartment-id $compartment_ocid --display-name=${clusterName}_vcn | jq -r '.data[] | .id'` + declare -a vcnidarray + vcnidarray=(${myvcn_id// /}) + myip=`oci lb load-balancer list --compartment-id $compartment_ocid |jq -r '.data[] | .id'` + mysubnets=`oci network subnet list --vcn-id=${vcnidarray[0]} --display-name=${clusterName}-LB-${1} --compartment-id $compartment_ocid | jq -r '.data[] | .id'` + + declare -a iparray + declare -a mysubnetsidarray + mysubnetsidarray=(${mysubnets// /}) + + iparray=(${myip// /}) + vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') + for k in "${mysubnetsidarray[@]}" + do + for i in "${iparray[@]}" + do + lb=`oci lb load-balancer get --load-balancer-id=$i` + echo "deleting lb with id $i $lb" + if [[ (-z "${lb##*$vcn_cidr_prefix*}") || (-z "${lb##*$k*}") ]] ;then + echo "deleting lb with id $i" + sleep 60 + oci lb load-balancer delete --load-balancer-id=$i --force || true + fi + done + done + myip=`oci lb load-balancer list --compartment-id $compartment_ocid |jq -r '.data[] | .id'` + iparray=(${myip// /}) + for k in "${mysubnetsidarray[@]}" + do + for i in "${iparray[@]}" + do + lb=`oci lb load-balancer get --load-balancer-id=$i` + echo "deleting lb with id $i $lb" + if [[ (-z "${lb##*$vcn_cidr_prefix*}") || (-z "${lb##*$k*}") ]] ;then + echo "deleting lb with id $i" + sleep 60 + oci lb load-balancer delete --load-balancer-id=$i --force || true + fi + done + done +} + +deleteOKE() { + cd ${terraform_script_dir} + terraform init -var-file=${terraform_script_dir}/${clusterName}.tfvars + terraform plan -var-file=${terraform_script_dir}/${clusterName}.tfvars + terraform destroy -auto-approve -var-file=${terraform_script_dir}/${clusterName}.tfvars +} + + +#MAIN +oci_property_file=${1:-$PWD/oci.props} +terraform_script_dir=${2:-$PWD} +availability_domain=${3:-mFEn:PHX-AD-1} +clusterName=$(prop 'okeclustername') +compartment_ocid=$(prop 'compartment.ocid') +vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') +export KUBECONFIG=${terraform_script_dir}/${clusterName}_kubeconfig +export PATH=${terraform_script_dir}/terraforminstall:$PATH +echo 'Deleting cluster' +#check and cleanup any left over running Load Balancers +out=$(cleanupLB Subnet01 && :) +echo $out +out=$(cleanupLB Subnet02 && :) +echo $out +deleteOKE || true diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/providers.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/providers.tf new file mode 100644 index 00000000000..45f3173e1e8 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/providers.tf @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +provider "oci" { + fingerprint = var.api_fingerprint + private_key_path = var.api_private_key_path + region = lookup(local.regions,var.home_region) + tenancy_ocid = var.tenancy_id + user_ocid = var.user_id + alias = "home" + ignore_defined_tags = ["Oracle-Tags.CreatedBy", "Oracle-Tags.CreatedOn"] +} + +provider "oci" { + fingerprint = var.api_fingerprint + private_key_path = var.api_private_key_path + region = lookup(local.regions,lookup(lookup(var.clusters,"c1"),"region")) + tenancy_ocid = var.tenancy_id + user_ocid = var.user_id + alias = "c1" + ignore_defined_tags = ["Oracle-Tags.CreatedBy", "Oracle-Tags.CreatedOn"] +} + + diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/cloud-init.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/cloud-init.sh new file mode 100644 index 00000000000..a6ace5460b8 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/cloud-init.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + + + +modprobe br_netfilter +modprobe nf_nat +modprobe xt_REDIRECT +modprobe xt_owner +modprobe iptable_nat +modprobe iptable_mangle +modprobe iptable_filter + +/usr/libexec/oci-growfs -y + +timedatectl set-timezone Australia/Sydney + +'curl --fail -H "Authorization: Bearer Oracle" -L0 http://169.254.169.254/opc/v2/instance/metadata/oke_init_script | base64 --decode >/var/run/oke-init.sh' + +bash -x /var/run/oke-init.sh + +touch /var/log/oke.done diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/generate_kubeconfig.template.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/generate_kubeconfig.template.sh new file mode 100644 index 00000000000..b905b5437ea --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/generate_kubeconfig.template.sh @@ -0,0 +1,5 @@ +#!/usr/bin/bash +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +oci ce cluster create-kubeconfig --cluster-id ${cluster_id} --file $HOME/.kube/config --region ${region} --token-version 2.0.0 --kube-endpoint ${endpoint} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/kubeconfig_set_credentials.template.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/kubeconfig_set_credentials.template.sh new file mode 100644 index 00000000000..44f66429475 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/kubeconfig_set_credentials.template.sh @@ -0,0 +1,12 @@ +#!/usr/bin/bash +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +$${KUBERNETES_CLI:-kubectl} config set-credentials "user-${cluster_id_11}" --exec-command="$HOME/bin/token_helper.sh" \ + --exec-arg="ce" \ + --exec-arg="cluster" \ + --exec-arg="generate-token" \ + --exec-arg="--cluster-id" \ + --exec-arg="${cluster_id}" \ + --exec-arg="--region" \ + --exec-arg="${region}" diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/set_alias.template.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/set_alias.template.sh new file mode 100644 index 00000000000..0103b4f81de --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/set_alias.template.sh @@ -0,0 +1,5 @@ +#!/usr/bin/bash +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +kubectx ${cluster}=context-${cluster_id_11} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/token_helper.template.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/token_helper.template.sh new file mode 100644 index 00000000000..c7384e794a1 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/scripts/token_helper.template.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright 2024 Oracle Corporation and/or affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +CLUSTER=$5 +REGION=$7 + +TOKEN_FILE=$HOME/.kube/TOKEN-$CLUSTER + +if ! test -f "$TOKEN_FILE" || test $(( `date +%s` - `stat -L -c %Y $TOKEN_FILE` )) -gt 240; then + umask 022 + oci ce cluster generate-token --cluster-id "$CLUSTER" --region "$REGION" > $TOKEN_FILE +fi + +cat $TOKEN_FILE diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars b/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars new file mode 100755 index 00000000000..3a938d76f78 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars @@ -0,0 +1,56 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Template to generate TF variables file for cluster creation from property file oci.props +# +# User-specific vars - you can get these easily from the OCI console from your user page +# + +# provider + api_fingerprint = "@OCIAPIPUBKEYFINGERPRINT@" +# + api_private_key_path = "@OCIPRIVATEKEYPATH@" +# + home_region = "phoenix" # Use short form e.g. ashburn from location column https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm +# + tenancy_id = "@TENANCYOCID@" +# + user_id = "@USEROCID@" +# + cluster_name="@OKECLUSTERNAME@" + compartment_id = "@COMPOCID@" + vcn_id = "@VCNOCID@" + control_plane_subnet_id = "@PUBSUBNETOCID@" + pub_lb_id = "@PUBSUBNETOCID@" + worker_subnet_id = "@PRIVATESUBNETOCID@" + +#MountTarget +mount_target_ocid="@MOUNTTARGETOCID@" +# +# # ssh + ssh_private_key_path = "@NODEPOOLSSHPK@" + ssh_public_key_path = "@NODEPOOLSSHPUBKEY@" +# +# # clusters +# ## For regions, # Use short form e.g. ashburn from location column https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm +# ## VCN, Pods and services clusters must not overlap with each other and with those of other clusters. + clusters = { + c1 = { region = "phoenix", vcn = "10.1.0.0/16", pods = "10.201.0.0/16", services = "10.101.0.0/16", enabled = true } + } +# + kubernetes_version = "@OKEK8SVERSION@" +# + cluster_type = "basic" +# + oke_control_plane = "public" + node_shape = "@NODEPOOLSHAPE@" +nodepools = { + np1 = { + shape = "@NODEPOOLSHAPE@", + ocpus = 2, + memory = 64, + size = 2, + boot_volume_size = 150, + } +} + diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/templates.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/templates.tf new file mode 100644 index 00000000000..3b45962e98c --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/templates.tf @@ -0,0 +1,44 @@ +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +locals { + + all_cluster_ids = merge( + { c1 = one(element([module.c1[*].cluster_id], 0)) } + ) + + kubeconfig_templates = { + for cluster_name, cluster_id in local.all_cluster_ids : + cluster_name => templatefile("${path.module}/scripts/generate_kubeconfig.template.sh", + { + cluster_id = cluster_id + endpoint = var.oke_control_plane == "public" ? "PUBLIC_ENDPOINT" : "PRIVATE_ENDPOINT" + region = lookup(local.regions, lookup(lookup(var.clusters, cluster_name), "region")) + } + ) + } + + set_credentials_templates = { + for cluster_name, cluster_id in local.all_cluster_ids : + cluster_name => templatefile("${path.module}/scripts/kubeconfig_set_credentials.template.sh", + { + cluster_id = cluster_id + cluster_id_11 = substr(cluster_id, (length(cluster_id) - 11), length(cluster_id)) + region = lookup(local.regions, lookup(lookup(var.clusters, cluster_name), "region")) + } + ) + } + + set_alias_templates = { + for cluster_name, cluster_id in local.all_cluster_ids : + cluster_name => templatefile("${path.module}/scripts/set_alias.template.sh", + { + cluster = cluster_name + cluster_id_11 = substr(cluster_id, (length(cluster_id) - 11), length(cluster_id)) + } + ) + } + + token_helper_template = templatefile("${path.module}/scripts/token_helper.template.sh", {}) + +} diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/variables.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/variables.tf new file mode 100644 index 00000000000..2553a2576b6 --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/variables.tf @@ -0,0 +1,186 @@ +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# OCI Provider parameters +variable "api_fingerprint" { + default = "" + description = "Fingerprint of the API private key to use with OCI API." + type = string +} + +variable "api_private_key_path" { + default = "" + description = "The path to the OCI API private key." + type = string +} +variable "vcn_id" { + default = "" + description = "The vnc id." + type = string +} + +variable "home_region" { + description = "The tenancy's home region. Use the short form in lower case e.g. phoenix." + type = string +} + +variable "tenancy_id" { + description = "The tenancy id of the OCI Cloud Account in which to create the resources." + type = string +} + +variable "user_id" { + description = "The id of the user that Terraform will use to create the resources." + type = string + default = "" +} + +variable "control_plane_subnet_id" { + description = "The id of the control_plane_subnet_id." + type = string + default = "" +} + +variable "control_plane_is_public" { + default = true + description = "Whether the Kubernetes control plane endpoint should be allocated a public IP address to enable access over public internet." + type = bool +} + +variable "assign_public_ip_to_control_plane" { + default = true + description = "Whether to assign a public IP address to the API endpoint for public access. Requires the control plane subnet to be public to assign a public IP address." + type = bool +} +# General OCI parameters +variable "compartment_id" { + description = "The compartment id where to create all resources." + type = string +} + +# ssh keys +variable "ssh_private_key_path" { + default = "none" + description = "The path to ssh private key." + type = string +} + +variable "ssh_public_key_path" { + default = "none" + description = "The path to ssh public key." + type = string +} + +# clusters + +variable "clusters" { + description = "A map of cidrs for vcns, pods and services for each region" + type = map(any) + default = { + c1 = { region = "sydney", vcn = "10.1.0.0/16", pods = "10.201.0.0/16", services = "10.101.0.0/16", enabled = true } + } +} + +variable "cluster_name" { + default = "c1" + description = "The cluster name." + type = string +} + +variable "cluster_id" { + default = null + description = "An existing OKE cluster OCID when `create_cluster = false`." + type = string +} + +variable "create_cluster" { + default = true + description = "Whether to create the OKE cluster and dependent resources." + type = bool +} + +variable "kubernetes_version" { + default = "v1.29.1" + description = "The version of Kubernetes to use." + type = string +} + +variable "cluster_type" { + default = "basic" + description = "Whether to use basic or enhanced OKE clusters" + type = string + + validation { + condition = contains(["basic", "enhanced"], lower(var.cluster_type)) + error_message = "Accepted values are 'basic' or 'enhanced'." + } +} + +variable "mount_target_ocid" { + default = "public" +} + +variable "availability_domain" { + default = "mFEn:PHX-AD-1" +} + +variable "oke_control_plane" { + default = "public" + description = "Whether to keep all OKE control planes public or private." + type = string + + validation { + condition = contains(["public", "private"], lower(var.oke_control_plane)) + error_message = "Accepted values are 'public' or 'private'." + } +} + +variable "preferred_cni" { + default = "flannel" + description = "Whether to use flannel or NPN" + type = string + + validation { + condition = contains(["flannel", "npn"], lower(var.preferred_cni)) + error_message = "Accepted values are 'flannel' or 'npn'." + } +} + +# node pools +variable "timezone" { + type = string + description = "Preferred timezone of computes" + default = "Australia/Sydney" +} + +variable "worker_subnet_id" { type = string } +variable "pub_lb_id" { type = string } +variable "node_shape" { type = string } + +variable "nodepools" { + type = any + description = "Node pools for all clusters" + default = { + np1 = { + shape = "VM.Standard.E4.Flex", + ocpus = 2, + memory = 32, + size = 3, + boot_volume_size = 150, + } + } +} + +variable "output_detail" { + default = true + description = "Whether to include detailed output in state." + type = bool +} + +variable "cluster_ca_cert" { + default = null + description = "Base64+PEM-encoded cluster CA certificate for unmanaged instance pools. Determined automatically when 'create_cluster' = true or 'cluster_id' is provided." + type = string +} + + diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/versions.tf b/integration-tests/src/test/resources/oke/terraform/okemodule/versions.tf new file mode 100644 index 00000000000..1a71edf600b --- /dev/null +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +#Terraform and provider version to use +terraform { + required_providers { + oci = { + source = "oracle/oci" + } + } + required_version = ">= 1.0.0" +} From 68c83689b1bc80c88cd5b21bddcd23513c604510 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 17 Jun 2024 14:39:00 -0400 Subject: [PATCH 087/356] Dependency updates --- .github/workflows/release.yml | 2 +- operator-build-maven-plugin/pom.xml | 4 ++-- pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea59b19d18f..193c383bee6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: password: ${{ secrets.PUBLISH_SECRET }} - name: Build and push container image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index a9cedfdcb32..e97b847b6c7 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -30,7 +30,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.13.0 + 3.13.1 org.junit.jupiter @@ -59,7 +59,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.13.0 + 3.13.1 oracle.kubernetes:jsonschema-maven-plugin diff --git a/pom.xml b/pom.xml index 0f3638066f7..60b3013e938 100644 --- a/pom.xml +++ b/pom.xml @@ -680,7 +680,7 @@ 3.3.0 10.17.0 1.0 - 3.3.2 + 3.4.0 3.2.4 2.0.0.0 1.3.3 From bfe4838ee76cd4e9056d3ea29694bb566d07f5d1 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Tue, 18 Jun 2024 18:36:58 +0000 Subject: [PATCH 088/356] Jenkinsfile for OKD --- Jenkinsfile.okd | 511 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100644 Jenkinsfile.okd diff --git a/Jenkinsfile.okd b/Jenkinsfile.okd new file mode 100644 index 00000000000..9879274d510 --- /dev/null +++ b/Jenkinsfile.okd @@ -0,0 +1,511 @@ + +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// + +CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=wko-okd-wls-srg + H 2 * * * % MAVEN_PROFILE_NAME=wko-okd-wls-mrg + H 3 * * * % MAVEN_PROFILE_NAME=wko-okd-fmw-cert''' + +pipeline { + agent { label 'large' } + options { + timeout(time: 1800, unit: 'MINUTES') + disableConcurrentBuilds() + } + triggers { + // timer trigger for "nightly build" + parameterizedCron(env.JOB_NAME == 'okd-dev' ? + CRON_SETTINGS : '') + } + + tools { + maven 'maven-3.8.7' + jdk 'jdk21' + } + + environment { + ocir_host = "${env.WKT_OCIR_HOST}" + wko_tenancy = "${env.WKT_TENANCY}" + ocir_creds = 'wkt-ocir-creds' + + outdir = "${WORKSPACE}/staging" + result_root = "${outdir}/wl_k8s_test_results" + pv_root = "/export/wls" + nfs_server = "${env.OKD_NFS_SERVER}" + okd_ip = "${env.OKD_IP}" + + okdkub = 'okdconfig' + + start_time = sh(script: 'date +"%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" + } + + parameters { + string(name: 'BRANCH', + description: 'The branch for weblogic-kubernetes-operator project', + defaultValue: "release/4.2" + ) + + choice(name: 'MAVEN_PROFILE_NAME', + description: 'Profile to use in mvn command to run the tests. Possible values are integration-tests. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + choices: [ + 'wko-okd-wls-srg', + 'wko-okd-wls-mrg', + 'wko-okd-fmw-cert', + 'integration-tests' + ] + ) + + string(name: 'IT_TEST', + description: 'Comma separated list of individual It test classes to be run e.g., ItParameterizedDomain, ItMiiUpdateDomainConfig, ItMiiDynamicUpdate*, ItMiiMultiMode', + defaultValue: '' + ) + + string(name: 'OPERATOR_LOG_LEVEL', + description: 'The default log level is not set', + defaultValue: '' + ) + + string(name: 'KUBECTL_VERSION', + description: 'kubectl version', + defaultValue: '1.26.2' + ) + + string(name: 'HELM_VERSION', + description: 'Helm version', + defaultValue: '3.11.2' + ) + + choice(name: 'ISTIO_VERSION', + description: 'Istio version', + choices: [ + '1.17.2', + '1.16.1', + '1.13.2', + '1.12.6', + '1.11.1', + '1.10.4', + '1.9.9' + ] + ) + + booleanParam(name: 'PARALLEL_RUN', + description: 'Runs tests in parallel. Default is false, test classes run in parallel.', + defaultValue: false + ) + string(name: 'NUMBER_OF_THREADS', + description: 'Number of threads to run the classes in parallel, default is 2.', + defaultValue: "3" + ) + string(name: 'WDT_DOWNLOAD_URL', + description: 'URL to download WDT.', + defaultValue: 'https://github.com/oracle/weblogic-deploy-tooling/releases/latest' + ) + string(name: 'WIT_DOWNLOAD_URL', + description: 'URL to download WIT.', + defaultValue: 'https://github.com/oracle/weblogic-image-tool/releases/latest' + ) + string(name: 'REPO_REGISTRY', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + + choice(name: 'BASE_IMAGES_REPO', + choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], + description: 'Repository to pull the base images. Make sure to modify the image names if you are modifying this parameter value.' + ) + string(name: 'TEST_IMAGES_REPO', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + string(name: 'WEBLOGIC_IMAGE_NAME', + description: 'WebLogic base image name. Default is the image name in OCIR. Use middleware/weblogic for OCR.', + defaultValue: 'test-images/weblogic' + ) + string(name: 'WEBLOGIC_IMAGE_TAG', + description: '12.2.1.3 (12.2.1.3-ol7) , 12.2.1.3-dev (12.2.1.3-dev-ol7), 12.2.1.3-ol8, 12.2.1.3-dev-ol8, 12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '12.2.1.4' + ) + string(name: 'FMWINFRA_IMAGE_NAME', + description: 'FWM Infra image name. Default is the image name in OCIR. Use middleware/fmw-infrastructure for OCR.', + defaultValue: 'test-images/fmw-infrastructure' + ) + string(name: 'FMWINFRA_IMAGE_TAG', + description: 'FWM Infra image tag', + defaultValue: '12.2.1.4' + ) + string(name: 'DB_IMAGE_NAME', + description: 'Oracle DB image name. Default is the image name in OCIR, use database/enterprise for OCR.', + defaultValue: 'test-images/database/enterprise' + ) + string(name: 'DB_IMAGE_TAG', + description: 'Oracle DB image tag', + defaultValue: '12.2.0.1-slim' + ) + string(name: 'MONITORING_EXPORTER_BRANCH', + description: '', + defaultValue: 'main' + ) + string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', + description: '', + defaultValue: '2.1.3' + ) + string(name: 'PROMETHEUS_CHART_VERSION', + description: '', + defaultValue: '17.0.0' + ) + string(name: 'GRAFANA_CHART_VERSION', + description: '', + defaultValue: '6.44.11' + ) + booleanParam(name: 'COLLECT_LOGS_ON_SUCCESS', + description: 'Collect logs for successful runs. Default is false.', + defaultValue: false + ) + string(name: 'REMOTECONSOLE_VERSION', + description: 'RemoteConsole version.', + defaultValue: '2.4.7' + ) + } + + stages { + stage('Filter unwanted branches') { + stages { + stage('Workaround JENKINS-41929 Parameters bug') { + steps { + echo 'Initialize parameters as environment variables due to https://issues.jenkins-ci.org/browse/JENKINS-41929' + evaluate """${def script = ""; params.each { k, v -> script += "env.${k} = '''${v}'''\n" }; return script}""" + } + } + stage ('Echo environment') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + env|sort + java -version + mvn --version + python --version + docker version + ulimit -a + ulimit -aH + ''' + } + } + stage('Make Workspace bin directory') { + steps { + sh "mkdir -m777 -p ${WORKSPACE}/bin" + } + } + + stage('Build WebLogic Kubernetes Operator') { + steps { + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + sh "mvn -DtrimStackTrace=false clean install" + } + } + } + + stage('Install Helm') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + echo "$(pwd)" + + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=helm/helm-v${HELM_VERSION}.tar.gz --file=helm.tar.gz \ + --auth=instance_principal + tar zxf helm.tar.gz + mv linux-amd64/helm ${WORKSPACE}/bin/helm + rm -rf linux-amd64 + helm version + ''' + } + } + + stage('Run Helm installation tests') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + sh 'export PATH=${runtime_path} && mvn -pl kubernetes -P helm-installation-test verify' + } + } + } + + stage ('Install kubectl') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + echo "$(pwd)" + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=kubectl/kubectl-v${KUBECTL_VERSION} --file=${WORKSPACE}/bin/kubectl \ + --auth=instance_principal + chmod +x ${WORKSPACE}/bin/kubectl + kubectl version --client=true + ''' + } + } + + stage ('Install OpenShift CLI oc') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + wget -N https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest/linux/oc.tar.gz + tar xvf oc.tar.gz + mv oc ${WORKSPACE}/bin/ + echo "PATH: " $PATH + oc version + ''' + } + } + + stage('Preparing Integration Test Environment') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + withCredentials([file(credentialsId: "${okdkub}", variable: 'KUBECONFIG_FILE') + ]) { + + sh ''' + export NO_PROXY=${OKD_IP},$NO_PROXY + export no_proxy=${OKD_IP},$no_proxy + + echo "NO_PROXY:" $NO_PROXY + echo "no_proxy:" $no_proxy + + export PATH=${runtime_path} + export KUBECONFIG=${KUBECONFIG_FILE} + cat $KUBECONFIG + + KUBERNETES_CLI=${KUBERNETES_CLI:-kubectl} + + echo "Checking nodes" + ${KUBERNETES_CLI} get nodes -o wide + + mkdir -m777 -p ${result_root} + echo "Results will be in ${result_root}" + + echo "cleaning up k8s artifacts" + ${KUBERNETES_CLI} delete crd $(${KUBERNETES_CLI} get crd | grep weblogic) || true + + ${KUBERNETES_CLI} get ns --no-headers | awk '$1 ~ /^ns-/{print $1}' | xargs ${KUBERNETES_CLI} delete ns || true + ${KUBERNETES_CLI} get ns --no-headers | awk '/weblogic/{print $1}' | xargs ${KUBERNETES_CLI} delete ns || true + ${KUBERNETES_CLI} get ns --no-headers | awk '/test-/{print $1}' | xargs ${KUBERNETES_CLI} delete ns || true + ${KUBERNETES_CLI} delete pv domain1-weblogic-sample-pv --wait=false || true + ${KUBERNETES_CLI} delete pv domain2-weblogic-sample-pv --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testalertmanager --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafana --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheus --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testalertmanagertest1 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafanatest1 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheustest1 --wait=false || true + + ${KUBERNETES_CLI} delete pv pv-testalertmanagertest2 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafanatest2 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheustest2 --wait=false || true + + ${KUBERNETES_CLI} delete pv pv-testalertmanagertest3 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafanatest3 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheustest3 --wait=false || true + + ${KUBERNETES_CLI} get ingressroutes -A --no-headers | awk '/tdlbs-/{print $2}' | xargs ${KUBERNETES_CLI} delete ingressroute || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/ns-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/appscode/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/nginx-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/traefik-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/ns-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/appscode/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/nginx-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/traefik-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + + echo "pv_root: " ${pv_root} + + ''' + } + } + } + + stage('Run integration tests') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + + steps { + script { + currentBuild.description = "${GIT_BRANCH} ${MAVEN_PROFILE_NAME}" + def res = 0 + res = sh(script: ''' + if [ -z "${IT_TEST}" ] && [ "${MAVEN_PROFILE_NAME}" = "integration-tests" ]; then + echo 'ERROR: All tests cannot be run with integration-tests profile' + exit 1 + fi + ''', returnStatus: true) + if (res != 0 ) { + currentBuild.result = 'ABORTED' + error('Profile/ItTests Validation Failed') + } + } + + sh ''' + + export PATH=${runtime_path} + export OKD=true + export NFS_SERVER=${OKD_NFS_SERVER} + echo "NFS_SERVER is: " $NFS_SERVER + export pv_root="/export/wls" + mkdir -m777 -p "${WORKSPACE}/.mvn" + touch ${WORKSPACE}/.mvn/maven.config + + if [ -n "${IT_TEST}" ]; then + echo 'Overriding MAVEN_PROFILE_NAME to integration-test when running individual test(s)' + MAVEN_PROFILE_NAME="integration-tests" + echo "-Dit.test=\"${IT_TEST}\"" >> ${WORKSPACE}/.mvn/maven.config + fi + + echo "MAVEN_PROFILE_NAME:" $MAVEN_PROFILE_NAME + echo "PARALLEL_RUN:" $PARALLEL_RUN + echo "-Dwko.it.wle.download.url=\"${wle_download_url}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.result.root=\"${result_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.pv.root=\"${pv_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.k8s.nodeport.host=\"${K8S_NODEPORT_HOST}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.nfs.server=\"${NFS_SERVER}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.istio.version=\"${ISTIO_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DPARALLEL_CLASSES=\"${PARALLEL_RUN}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DNUMBER_OF_THREADS=\"${NUMBER_OF_THREADS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wdt.download.url=\"${WDT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wit.download.url=\"${WIT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.repo=\"${BASE_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.repo=\"${TEST_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.name=\"${WEBLOGIC_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.tag=\"${WEBLOGIC_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.name=\"${FMWINFRA_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.tag=\"${FMWINFRA_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.name=\"${DB_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.tag=\"${DB_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.branch=\"${MONITORING_EXPORTER_BRANCH}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.webapp.version=\"${MONITORING_EXPORTER_WEBAPP_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.grafana.chart.version=\"${GRAFANA_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.collect.logs.on.success=\"${COLLECT_LOGS_ON_SUCCESS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DOPERATOR_LOG_LEVEL=\"${OPERATOR_LOG_LEVEL}\"" >> ${WORKSPACE}/.mvn/maven.config + + echo "${WORKSPACE}/.mvn/maven.config contents:" + cat "${WORKSPACE}/.mvn/maven.config" + cp "${WORKSPACE}/.mvn/maven.config" "${result_root}" + + ''' + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + withCredentials([ + usernamePassword(credentialsId: "${ocir_creds}", usernameVariable: 'OCIR_USER', passwordVariable: 'OCIR_PASS'), + file(credentialsId: "${okdkub}", variable: 'KUBECONFIG_FILE') + ]) { + sh ''' + + export NO_PROXY=${OKD_IP},$NO_PROXY + export no_proxy=${OKD_IP},$no_proxy + + + echo "NO_PROXY:" $NO_PROXY + echo "no_proxy:" $no_proxy + + export PATH=${runtime_path} + export OKD="true" + export NFS_SERVER=${OKD_NFS_SERVER} + echo "NFS_SERVER is: " $NFS_SERVER + export pv_root="/export/wls" + + export KUBECONFIG=${KUBECONFIG_FILE} + cat $KUBECONFIG + echo "Checking nodes" + KUBERNETES_CLI=${KUBERNETES_CLI:-kubectl} + ${KUBERNETES_CLI} get nodes -o wide + + echo "K8S_NODEPORT_HOST:" $K8S_NODEPORT_HOST + + export BASE_IMAGES_REPO_USERNAME="${OCIR_USER}" + export BASE_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export BASE_IMAGES_REPO_EMAIL="noreply@oracle.com" + export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" + export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" + + + if [[ -n "${IT_TEST}" && "${IT_TEST}" != "**/It*" ]]; then + echo 'Overriding MAVEN_PROFILE_NAME to integration-test when running individual test(s)' + export MAVEN_PROFILE_NAME="integration-tests" + fi + echo "MAVEN_PROFILE_NAME:" $MAVEN_PROFILE_NAME + if ! mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/okdtest.log"; then + echo "integration-tests failed" + exit 1 + fi + ''' + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + + mkdir -m777 -p ${result_root}/kubelogs + mkdir -m777 -p "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + sudo mv -f ${result_root}/* "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + + ''' + + archiveArtifacts(artifacts: + "logdir/${BUILD_TAG}/wl_k8s_test_results/diagnostics/**/*,logdir/${BUILD_TAG}/wl_k8s_test_results/workdir/liftandshiftworkdir/**/*,integration-tests/target/failsafe-reports/*.xml") + junit(testResults: 'integration-tests/target/failsafe-reports/*.xml', allowEmptyResults: true) + } + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + + rm -rf ${WORKSPACE}/.mvn + + + ''' + } + } + } + + stage ('Sync') { + when { + anyOf { + branch 'release/4.2' + } + anyOf { + not { triggeredBy 'TimerTrigger' } + tag 'v*' + } + } + steps { + build job: "wkt-sync", parameters: [ string(name: 'REPOSITORY', value: 'weblogic-kubernetes-operator') ] + } + } + } +} From 4b2283891c627c7e94f4742b10d773e3418b9363 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Jun 2024 15:40:57 +0000 Subject: [PATCH 089/356] Merge branch 'aks-userguide-for-autoscaling' into 'main' update aks use guide for autoscaling. See merge request weblogic-cloud/weblogic-kubernetes-operator!4736 (cherry picked from commit 5543f8d437ec69af4ebbf4daf47b87a3519235dd) 29ae2e9c update aks use guide for autoscaling. --- .../content/managing-domains/aks/_index.md | 159 ++++++++++++------ 1 file changed, 104 insertions(+), 55 deletions(-) diff --git a/documentation/site/content/managing-domains/aks/_index.md b/documentation/site/content/managing-domains/aks/_index.md index bffdb26dfc4..5ad74a8cf86 100644 --- a/documentation/site/content/managing-domains/aks/_index.md +++ b/documentation/site/content/managing-domains/aks/_index.md @@ -53,17 +53,18 @@ Use the **Basics** blade to provide the basic configuration details for deployin | Field | Description | |-------|-------------| -| Accept defaults for optional configuration? | If you want to retain the default values for the optional configuration, such as **Name prefix for Managed Server**, **WebLogic Domain Name** and others, set the toggle button to **Yes**, and click **Next: Configure AKS cluster**. If you want to specify different values for the optional configuration, set the toggle button to **No**, and enter the following details. | +| Accept defaults for optional configuration? | If you want to retain the default values for the optional configuration, such as **Name prefix for Managed Server**, **WebLogic Domain Name** and others, set the toggle button to **Yes**, and click **Next** to configure AKS cluster. If you want to specify different values for the optional configuration, set the toggle button to **No**, and enter the following details. | | Name prefix for Managed Server | Enter a prefix for the Managed Server name. | | WebLogic Domain Name | Enter the name of the domain that will be created by the offer. | +| WebLogic Domain UID | Enter the UID of the domain that will be created by the offer. | | Maximum dynamic cluster size | The maximum size of the dynamic WebLogic cluster created. | |Custom Java Options to start WebLogic Server | Java VM arguments passed to the invocation of WebLogic Server. For more information, see the [FAQ]({{< relref "/faq/resource-settings/_index.md" >}}). | -When you are satisfied with your selections, select **Next : Configure AKS cluster**. +When you are satisfied with your selections, select **Next** and open **AKS** blade. -### Configure AKS cluster +### AKS -Use the **Configure AKS Cluster** blade to configure fundamental details of how Oracle WebLogic Server runs on AKS. To do this, enter the values for the fields listed in the following tables. +Use the **AKS** blade to configure fundamental details of how Oracle WebLogic Server runs on AKS. To do this, enter the values for the fields listed in the following tables. #### Azure Kubernetes Service @@ -72,11 +73,9 @@ In this section, you can configure some options about the AKS which will run Web | Field | Description | |-------|-------------| |Create a new AKS cluster| If set to **Yes**, the deployment will create a new AKS cluster resource in the specified resource group. If set to **No**, you have the opportunity to select an existing AKS cluster, into which the deployment is configured. Note: the offer assumes the existing AKS cluster has no WebLogic related deployments. | -|Use latest supported AKS Kubernetes version| The currently supported version is **1.24.3**. Oracle tracks the AKS release versions in [Supported Kubernetes versions in Azure Kubernetes Service (AKS)](https://docs.microsoft.com/en-us/azure/aks/supported-kubernetes-versions). After a new version emerges, Oracle qualifies WLS on AKS against that version and will update the offer to that version. Please see "WebLogic Kubernetes ToolKit Support Policy (Doc ID 2790123.1)" and "Support for Oracle Fusion Middleware on Azure and Oracle Linux (Doc ID 2914257.1)" in My Oracle Support for the Oracle support policy for WLS on Kubernetes, including AKS.| -| Node count | The initial number of nodes in the AKS cluster. This value can be changed after deployment. For information, see [Scaling]({{< relref "/managing-domains/domain-lifecycle/scaling.md" >}}). | +| Minimum node count | The minimum node count in the AKS cluster. This value can be changed after deployment. For information, see [AKS autoscaler](https://learn.microsoft.com/azure/aks/cluster-autoscaler). | +| Maximum node count | The maximum node count in the AKS cluster. This value can be changed after deployment. For information, see [AKS autoscaler](https://learn.microsoft.com/azure/aks/cluster-autoscaler). | | Node size | The default VM size is 2x Standard DSv2, 2 vcpus, 7 GB memory. If you want to select a different VM size, select **Change Size**, select the size from the list (for example, A3) on the Select a VM size page, and select **Select**. For more information about sizing the virtual machine, see the [Azure documentation on Sizes](https://docs.microsoft.com/azure/cloud-services/cloud-services-sizes-specs).| -|Enable Container insights| If selected, cause the deployment to create an Azure Monitoring workspace and connect it to the AKS cluster as the Azure Monitoring Agent. Azure Monitoring Agent is a tool that collects data and sends it to Azure Container Insights. Container insights gives you performance visibility by collecting memory and processor metrics from controllers, nodes, and containers that are available in Kubernetes through the Metrics API. Container logs are also collected. Metrics are written to the metrics store and log data is written to the logs store associated with your Log Analytics workspace. For more information, see [Azure Monitor Agent overview](/azure/azure-monitor/agents/agents-overview) and [Container insights overview](https://aka.ms/wls-aks-container-insights). | -|Create Persistent Volume using Azure File share service|If selected, an Azure Storage Account and an Azure Files share will be provisioned. The file system type is NFS. The name of the Azure Files share is **weblogic**. The mount point is `/shared` as a persistent volume in the nodes of the AKS cluster. For more information, see [Oracle WebLogic Server persistent storage]({{< relref "/managing-domains/persistent-storage/_index.md" >}}) and [persistent volume with Azure Files share on AKS](https://docs.microsoft.com/azure/aks/azure-files-volume).| #### Image selection @@ -84,34 +83,42 @@ In this section, you can configure the image that is deployed using the model-in | Field | Description | |-------|-------------| -| Use a pre-existing WebLogic Server Docker image from Oracle Container Registry (OCR)? | If set to **Yes**, the subsequent options are constrained to allow only selecting from a set of pre-existing WebLogic Server Docker images stored in the Oracle Container Registry. If set to **No**, the user may refer to a pre-existing Azure Container Registry, and must specify the Docker tag of the WebLogic Server image within that registry that will be used to create the domain. The specified image is assumed to be compatible with the WebLogic Kubernetes Operator. This allows the use of custom images, such as images with a specific one-off patch or Oracle quarterly patches (PSUs). For more about WebLogic Server images, see [WebLogic images]({{< relref "/base-images/_index.md" >}}).| |Create a new Azure Container Registry to store application images?|If set to **Yes**, the offer will create a new Azure Container Registry (ACR) to hold the images for use in the deployment. If set to **No**, you must specify an existing ACR. In this case, you must be sure the selected ACR has the admin account enabled. For details, please see [Admin account](https://docs.microsoft.com/azure/container-registry/container-registry-authentication?tabs=azure-cli#admin-account). | -| Select existing ACR instance | This option is shown only if **Use a pre-existing WebLogic Server Docker image from Oracle Container Registry?** is set to **No**. If visible, select an existing Acure Container Registry instance. | -| Please provide the image path | This option is shown only if **Use a pre-existing WebLogic Server Docker image from Oracle Container Registry?** is set to **No**. If visible, the value must be a fully qualified Docker tag of an image within the specified ACR. | +| Select ACR instance | This option is shown if **Create a new Azure Container Registry to store application images?** is set to **No**. If visible, select an existing Acure Container Registry instance. | | Username for Oracle Single Sign-On authentication | The Oracle Single Sign-on account user name for which the Terms and Restrictions for the selected WebLogic Server image have been accepted. | | Password for Oracle Single Sign-On authentication | The password for that account. | | Confirm password | Re-enter the value of the preceding field. | If 'Yes' is selected; the deployment process will pull from the CPU WebLogic Server image repository in the OCR. If 'No' is selected the deployment process will pull from the WebLogic Server image repository in OCR. | -| Is the specified SSO account associated with an active Oracle support contract? | If set to **Yes**, you must accept the license agreement in the `middleware/weblogic_cpu` repository. If set to **No**, you must accept the license agreement in the `middleware/weblogic`. Steps to accept the license agreement: log in to the [Oracle Container Registry](https://container-registry.oracle.com/); navigate to the `middleware/weblogic_cpu` and `middleware/weblogic` repository; accept license agreement. See this [document](https://aka.ms/wls-aks-ocr-doc) for more information. | -| Select WebLogic Server Docker tag | Select one of the supported images. | +| Select the type of WebLogic Server Images. | If set to **Patched WebLogic Server Images**, you must accept the license agreement in the `middleware/weblogic_cpu` repository. If set to **General WebLogic Server Images**, you must accept the license agreement in the `middleware/weblogic`. Steps to accept the license agreement: log in to the [Oracle Container Registry](https://container-registry.oracle.com/); navigate to the `middleware/weblogic_cpu` and `middleware/weblogic` repository; accept license agreement. See this [document](https://aka.ms/wls-aks-ocr-doc) for more information. | +| Select desired combination of WebLogic Server, JDK and Operating System or fully qualified Docker tag | Select one of the supported images. | -#### Java EE Application +#### Application In this section you can deploy a Java EE Application along with the WebLogic Server deployment. | Field | Description | |-------|-------------| -| Deploy your application package? | If set to **Yes**, you must specify a Java EE WAR, EAR, or JAR file suitable for deployment with the selected version of WebLogic Server. If set to **No**, no application is deployed.| +| Deploy an application? | If set to **Yes**, you must specify a Java EE WAR, EAR, or JAR file suitable for deployment with the selected version of WebLogic Server. If set to **No**, no application is deployed.| | Application package (.war,.ear,.jar) | With the **Browse** button, you can select a file from a pre-existing Azure Storage Account and Storage Container within that account. To learn how to create a Storage Account and Container, see [Create a storage account](https://docs.microsoft.com/azure/storage/common/storage-account-create?tabs=azure-portal) and [Create a Storage Container and upload application files](https://docs.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-portal). | -| Fail deployment if application does not become ACTIVE. | If selected, the deployment will wait for the deployed application to reach the **ACTIVE** state and fail the deployment if it does not. For more details, see the [Oracle documentation](https://aka.ms/wls-aks-deployment-state). | | Number of WebLogic Managed Server replicas | The initial value of the `replicas` field of the Domain. For information, see [Scaling]({{< relref "/managing-domains/domain-lifecycle/scaling.md" >}}). | -When you are satisfied with your selections, select **Next : TLS/SSL configuration**. +#### Advanced -### TLS/SSL configuration +| Field | Description | +|-------|-------------| +| Show advanced configuration? | If you want to retain the default values for the optional configuration, such as **Enable Container insights**, **Create Persistent Volume using Azure File share service** and others, set the toggle button to **No**, and click **Next** to configure TLS/SSL. If you want to specify different values for the optional configuration, set the toggle button to **Yes**, and enter the following details. | +|Enable Container insights| If selected, cause the deployment to create an Azure Monitoring workspace and connect it to the AKS cluster as the Azure Monitoring Agent. Azure Monitoring Agent is a tool that collects data and sends it to Azure Container Insights. Container insights gives you performance visibility by collecting memory and processor metrics from controllers, nodes, and containers that are available in Kubernetes through the Metrics API. Container logs are also collected. Metrics are written to the metrics store and log data is written to the logs store associated with your Log Analytics workspace. For more information, see [Azure Monitor Agent overview](/azure/azure-monitor/agents/agents-overview) and [Container insights overview](https://aka.ms/wls-aks-container-insights). | +|Create Persistent Volume using Azure File share service|If selected, an Azure Storage Account and an Azure Files share will be provisioned. The file system type is NFS. The name of the Azure Files share is **weblogic**. The mount point is `/shared` as a persistent volume in the nodes of the AKS cluster. For more information, see [Oracle WebLogic Server persistent storage]({{< relref "/managing-domains/persistent-storage/_index.md" >}}) and [persistent volume with Azure Files share on AKS](https://docs.microsoft.com/azure/aks/azure-files-volume).| +| Bring your own WebLogic Server Docker image from Azure Container Registry? | If check the checkbox, the subsequent options are constrained to allow only selecting from a set of pre-existing WebLogic Server Docker images stored in the Oracle Container Registry. | +| Select existing ACR instance | This option is shown only if **Bring your own WebLogic Server Docker image from Azure Container Registry?** is checked. If visible, select an existing Acure Container Registry instance. | +| Please provide the image path | This option is shown only if **Bring your own WebLogic Server Docker image from Azure Container Registry?** is checked. If visible, the value must be a fully qualified Docker tag of an image within the specified ACR. | + +When you are satisfied with your selections, select **Next** and open **TLS/SSL** blade. -With the **TLS/SSL configuration** blade, you can configure Oracle WebLogic Server Administration Console on a secure HTTPS port, with your own SSL certificate provided by a Certifying Authority (CA). See [Oracle WebLogic Server Keystores configuration](https://aka.ms/arm-oraclelinux-wls-ssl-configuration) for more information. +### TLS/SSL -Select **Yes** or **No** for the option **Configure WebLogic Server Administration Console, Remote Console, and cluster to use HTTPS (Secure) ports, with your own TLS/SSL certificate.** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by selecting **Next : Networking**. If you select **Yes**, you can choose to provide the required configuration details by either uploading existing keystores or by using keystores stored in Azure Key Vault. +With the **TLS/SSL** blade, you can configure Oracle WebLogic Server Administration Console on a secure HTTPS port, with your own SSL certificate provided by a Certifying Authority (CA). See [Oracle WebLogic Server Keystores configuration](https://aka.ms/arm-oraclelinux-wls-ssl-configuration) for more information. + +Select **Yes** or **No** for the option **Configure WebLogic Server Administration Console, Remote Console, and cluster to use HTTPS (Secure) ports, with your own TLS/SSL certificate.** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by selecting **Next**. If you select **Yes**, you can choose to provide the required configuration details by either uploading existing keystores or by using keystores stored in Azure Key Vault. If you want to upload existing keystores, select **Upload existing KeyStores** for the option **How would you like to provide required configuration**, and enter the values for the fields listed in the following table. @@ -148,33 +155,21 @@ If you want to use keystores that are stored in Azure Key Vault, select **Use Ke | The name of the secret in the specified Key Vault whose value is the passphrase for the Trust KeyStore | Enter the name of the Azure Key Vault secret that holds the value of the the passphrase for the trust keystore. | | The Trust KeyStore type (JKS,PKCS12) | Select the type of custom trust keystore. The supported values are JKS and PKCS12. | -When you are satisfied with your selections, select **Next : Networking**. - -### Networking - -Use this blade to configure options for load balancing and ingress controller. - -#### Standard Load Balancer service - -Selecting **Yes** here will cause the offer to provision the Azure Load Balancer as a Kubernetes load balancer service. For more information on the Standard Load Balancer see [Use a public Standard Load Balancer in Azure Kubernetes Service (AKS)](https://aka.ms/wls-aks-standard-load-balancer). You can still deploy an Azure Application Gateway even if you select **No** here. - -If you select **Yes**, you have the option of configuring the Load Balancer as an internal Load Balancer. For more information on Azure internal load balancers see [Use an internal load balancer with Azure Kubernetes Service (AKS)](https://aka.ms/wls-aks-internal-load-balancer). - -If you select **Yes**, you must fill in the following table to map the services to load balancer ports. +When you are satisfied with your selections, select **Next** and open **Load balancing** blade. -**Service name prefix** column: +### Load balancing -You can fill in any valid value in this column. +Use this blade to configure load balancing. There are three options: -**Target** and **Port** column: - -For the ports, the recommended values are the usual 7001 for the **admin-server** and 8001 for the **cluster-1**. +* Application Gateway Ingress Controller. +* Standard Load Balancer service. +* No Load Balancer. #### Application Gateway Ingress Controller In this section, you can create an Azure Application Gateway instance as the ingress controller of your WebLogic Server. This Application Gateway is pre-configured for end-to-end-SSL with TLS termination at the gateway using the provided SSL certificate and load balances across your cluster. -Select **Yes** or **No** for the option **Connect to Azure Application Gateway?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by selecting **Next : DNS Configuration >**. If you select **Yes**, you must specify the details required for the Application Gateway integration by entering the values for the fields as described next. +Select **Application Gateway Ingress Controller** for the option **Load Balancing Options** based on your preference. You must specify the details required for the Application Gateway integration by entering the values for the fields as described next. You can specify a virtual network for the application gateway. To do this, enter the values for the fields listed in the following tables. @@ -184,13 +179,18 @@ You can specify a virtual network for the application gateway. To do this, enter |-------|-------------| | Virtual network | Select a virtual network in which to place the application gateway. Make sure your virtual network meets the requirements in [Application Gateway virtual network and dedicated subnet](https://docs.microsoft.com/en-us/azure/application-gateway/configuration-infrastructure#virtual-network-and-dedicated-subnet). | | Subnet | An application gateway is a dedicated deployment in your virtual network. Within your virtual network, a dedicated subnet is required for the application gateway. See [Application Gateway virtual network and dedicated subnet](https://docs.microsoft.com/en-us/azure/application-gateway/configuration-infrastructure#virtual-network-and-dedicated-subnet). | -| Configure frontend IP with private IP address | If set to **Yes**, the Azure Marketplace offer will pick one of the available IP addresses from the application gateway subnet to be the private front-end IP address. For more information, see [Application Gateway front-end IP address configuration](https://docs.microsoft.com/en-us/azure/application-gateway/configuration-front-end-ip). | You must select one of the following three options, each described in turn. +* Generate a self-signed front-end certificate: Generate a self-signed front-end certificate and apply it during deployment. * Upload a TLS/SSL certificate: Upload the pre-signed certificate now. * Identify an Azure Key Vault: The Key Vault must already contain the certificate and its password stored as secrets. -* Generate a self-signed front-end certificate: Generate a self-signed front-end certificate and apply it during deployment. + +**Generate a self-signed frontend certificate** + +| Field | Description | +|-------|-------------| +| Trusted root certificate(.cer, .cert) | A trusted root certificate is required to allow back-end instances in the application gateway. The root certificate is a Base-64 encoded X.509(.CER) format root certificate. | **Upload a TLS/SSL certificate** @@ -211,27 +211,37 @@ You must select one of the following three options, each described in turn. | The name of the secret in the specified Key Vault whose value is the password for the front-end TLS/SSL certificate | Enter the name of the Azure Key Vault secret that holds the value of the password for the application gateway front-end SSL certificate. | | The name of the secret in the specified Key Vault whose value is the trusted root certificate data | A trusted root certificate is required to allow back-end instances in the application gateway. Enter the name of the Azure Key Vault secret that holds the value of the application gateway trusted root certificate data. Follow [Store the TLS/SSL certificate in the Key Vault](#store-the-tlsssl-certificate-in-the-key-vault) to upload the certificate to Azure Key Vault. | -**Generate a self-signed frontend certificate** - -| Field | Description | -|-------|-------------| -| Trusted root certificate(.cer, .cert) | A trusted root certificate is required to allow back-end instances in the application gateway. The root certificate is a Base-64 encoded X.509(.CER) format root certificate. | - Regardless of how you provide the certificates, there are several other options when configuring the Application Gateway, as described next. | Field | Description | |-------|-------------| -|Enable cookie based affinity | Select this box to enable cookie based affinity (sometimes called "sticky sessions"). For more information, see [Enable Cookie based affinity with an Application Gateway](https://docs.microsoft.com/azure/application-gateway/ingress-controller-cookie-affinity). | +|Disable cookie based affinity | Select this box to disable cookie based affinity (sometimes called "sticky sessions"). For more information, see [Enable Cookie based affinity with an Application Gateway](https://docs.microsoft.com/azure/application-gateway/ingress-controller-cookie-affinity). | | Create ingress for Administration Console. | Select **Yes** to create an ingress for the Administration Console with the path `/console`. | | Create ingress for WebLogic Remote Console. | Select **Yes** to create an ingress for the Remote Console with the path `/remoteconsole`. | -When you are satisfied with your selections, select **Next : DNS Configuration**. +#### Standard Load Balancer service -### DNS Configuration +Selecting **Yes** here will cause the offer to provision the Azure Load Balancer as a Kubernetes load balancer service. For more information on the Standard Load Balancer see [Use a public Standard Load Balancer in Azure Kubernetes Service (AKS)](https://aka.ms/wls-aks-standard-load-balancer). You can still deploy an Azure Application Gateway even if you select **No** here. + +If you select **Yes**, you have the option of configuring the Load Balancer as an internal Load Balancer. For more information on Azure internal load balancers see [Use an internal load balancer with Azure Kubernetes Service (AKS)](https://aka.ms/wls-aks-internal-load-balancer). + +If you select **Yes**, you must fill in the following table to map the services to load balancer ports. + +**Service name prefix** column: + +You can fill in any valid value in this column. + +**Target** and **Port** column: + +For the ports, the recommended values are the usual 7001 for the **admin-server** and 8001 for the **cluster-1**. -With the **DNS Configuration** blade, you can provision the Oracle WebLogic Server Administration Console using a custom DNS name. +When you are satisfied with your selections, select **Next** and open **DNS** blade. -Select **Yes** or **No** for the option **Configure Custom DNS Alias?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by selecting **Next : Database >**. If you select **Yes**, you must choose either to configure a custom DNS alias based on an existing Azure DNS zone, or create an Azure DNS zone and a custom DNS alias. This can be done by selecting **Yes** or **No** for the option **Use an existing Azure DNS Zone**. +### DNS + +With the **DNS** blade, you can provision the Oracle WebLogic Server Administration Console using a custom DNS name. + +Select **Yes** or **No** for the option **Custom DNS Alias?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by selecting **Next** to configure database. If you select **Yes**, you must choose either to configure a custom DNS alias based on an existing Azure DNS zone, or create an Azure DNS zone and a custom DNS alias. This can be done by selecting **Yes** or **No** for the option **Use an existing Azure DNS Zone**. {{% notice note %}} For more information about the DNS zones, see [Overview of DNS zones and records](https://docs.microsoft.com/azure/dns/dns-zones-records). @@ -258,11 +268,11 @@ See the preceding table for the description of these fields. In the case of creating an Azure DNS zone and a custom DNS alias, you must perform the DNS domain delegation at your DNS registry post deployment. See [Delegation of DNS zones with Azure DNS](https://docs.microsoft.com/azure/dns/dns-domain-delegation). {{% /notice %}} -When you are satisfied with your selections, select **Next : Database**. +When you are satisfied with your selections, select **Next** and open **Database** blade. ### Database -Use the Database blade to configure Oracle WebLogic Server to connect to an existing database. Select **Yes** or **No** for the option **Connect to Database?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by clicking **Next : Review + create >**. If you select **Yes**, you must specify the details of your database by entering the values for the fields listed in the following table. +Use the Database blade to configure Oracle WebLogic Server to connect to an existing database. Select **Yes** or **No** for the option **Connect to Database?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by clicking **Next**. If you select **Yes**, you must specify the details of your database by entering the values for the fields listed in the following table. | Field | Description | |-------|-------------| @@ -284,7 +294,44 @@ If you select **Other** as the database type, there are some additional values y | DataSource driver name | The fully qualified Java class name of the JDBC driver. | | Test table name | The name of the database table to use when testing physical database connections. This value depends on the specified database. Some suggested values include the following. {{< line_break >}}{{< line_break >}} • For Oracle, use `SQL ISVALID`. {{< line_break >}} • For PostgreSQL, SQL Server and MariaDB use `SQL SELECT 1`. {{< line_break >}} • For Informix use `SYSTABLES`.| -When you are satisfied with your selections, select **Next : Review + create**. +When you are satisfied with your selections, select **Next** and open **Autoscaling** blade. + +### Autoscaling + +Use the Autoscaling blade to configure metric that scales the WebLogic cluster. Select **Yes** or **No** for the option **Provision resources for horizontal autoscaling?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by clicking **Review + create**. If you select **Yes**, you must specify the details of your autoscaling option and autoscaling settings. + +You must select one of the following two options, each described in turn. + +* Kubernetes Metrics Server (simple autoscaling): this option configures and runs Kubernetes Horizontal Pod Autoscaler (HPA) to scale a WebLogic cluster, based on the CPU or memory utilization. +* WebLogic Monitoring Exporter (advanced autoscaling): this option allows you to create Java metric aware KEDA scaling rules. + +#### Kubernetes Metrics Server (simple autoscaling) + +| Field | Description | +|-------|-------------| +| Select metric | There are two options:{{< line_break >}}{{< line_break >}} • Average CPU Utilization {{< line_break >}} • Average Memory Utilization | +| Average CPU Utilization | Pick average CPU utilization in percent. The HPA autoscales WebLogic Server instances from a minimum of 1 cluster members up to maximum of cluster members, and the scale up or down action occur when the average CPU is consistently over the utilization. | +| Average Memory Utilization | Pick average memory utilization in percent. The HPA autoscales WebLogic Server instances from a minimum of 1 cluster members up to maximum of cluster members, and the scale up or down action occur when the average memory is consistently over the utilization.| + +#### WebLogic Monitoring Exporter (advanced autoscaling) + +This option installs all the software necessary to allow you to create Java metric aware KEDA scaling rules. The offer provisions the following deployments. Right-click and select **Open Link in New Tab** to follow links: + +* [Install WebLogic Monitoring Exporter to scrape WebLogic Server metrics](https://aka.ms/wls-exporter). +* [Install AKS Prometheus metrics addon](https://aka.ms/aks-enable-monitoring). +* [Feed WebLogic Server metrics to Azure Monitor Managed Service for Prometheus](https://aka.ms/aks-prometheus-metrics-scrape-configuration). +* [Integrate KEDA with AKS cluster](https://aka.ms/aks-integrate-keda). + +After the provisioning is completed, you can create KEDA scaling rules. A sample rule is provided in the deployment outputs. The following steps show how to see the sample rule. + +* View the resource group for this deployment in the Azure portal. +* In the **Settings** section, select **Deployments**. +* Select the oldest deployment. The name of the deployment looks similar to **oracle.20210620-wls-on-aks**. +* Select **Outputs**. +* The **shellCmdtoOutputKedaScalerSample** value is the base64 string of a scaler sample. Copy the value and run it in your terminal. +* For guidance on how to complete the configuration, see [Tutorial: Migrate Oracle WebLogic Server to AKS with KEDA scaler based on Prometheus Metrics](https://aka.ms/wls-aks-keda-scaler). + +When you are satisfied with your selections, select **Review + create**. ### Review + create @@ -313,7 +360,9 @@ After clicking **Create** to create this offer, you will go to the **Deployment | `clusterExternalSecuredUrl` | This output is not always present:{{< line_break >}}1. You must configure [Networking](#networking) to enable the Azure Load Balancer service or Azure Application Gateway Ingress Controller for the WLS cluster.{{< line_break >}}2. The TLS/SSL certificate used is configured by filling out [TLS/SSL configuration](#tlsssl-configuration).{{< line_break >}}{{< line_break >}}This is a fully qualified, public link to the WLS cluster. You can access your application with `${clusterExternalUrl}` from the public Internet.{{< line_break >}}Sample value: `https://contoso.azure.com/`. | | `clusterT3InternalUrl` | This output is not always present:{{< line_break >}}1. You must [create/update the WLS cluster with advanced configuration](https://oracle.github.io/weblogic-azure/aks/).{{< line_break >}}2. You must enable custom T3 channel by setting `enableClusterT3Tunneling=true`.{{< line_break >}}{{< line_break >}}This is a fully qualified, private link to custom T3 channel of the WLS cluster. | | `clusterT3ExternalEndpoint` | This output is not always present:{{< line_break >}}1. You must [create/update the WLS cluster with advanced configuration](https://oracle.github.io/weblogic-azure/aks/).{{< line_break >}}2. You must enable custom T3 channel by setting `enableClusterT3Tunneling=true`.{{< line_break >}}3. You must configure [Networking](#networking) to enable the Azure Load Balancer service for the WLS cluster.{{< line_break >}}{{< line_break >}}This is a fully qualified, public link to custom T3 channel of the WLS cluster.{{< line_break >}}Sample value:`http://20.4.56.3:8005/` | +| `kedaScalerServerAddress` | This output is the server address of that saves the WLS metrics. KEDA is able to access and retrieve metric from the address.| | `shellCmdtoConnectAks` | AZ CLI command to connect to the AKS cluster.{{< line_break >}}Sample value: {{< line_break >}}`az account set{{< line_break >}} --subscription ;{{< line_break >}} az aks get-credentials --resource-group contoso-rg{{< line_break >}} --name contosoakscluster`| +| `shellCmdtoOutputKedaScalerSample` | Sell command to display the the base64 string of a scaler sample.{{< line_break >}}Sample value: {{< line_break >}}`echo -e YXBpVm...XV0aAo= \| base64 -d > scaler.yaml` | | `shellCmdtoOutputWlsDomainYaml` | Shell command to display the base64 encoded string of the WLS domain resource definition.{{< line_break >}}Sample value: {{< line_break >}}`echo -e YXBpV...mVCg== \| base64 -d > domain.yaml` | | `shellCmdtoOutputWlsImageModelYaml` | Shell command to display the base64 encoded string of the WLS [image model]({{< relref "/managing-domains/model-in-image/model-files.md" >}}).{{< line_break >}}Sample value:{{< line_break >}}`echo -e IyBDb...3EnC \| base64 -d > model.yaml`| | `shellCmdtoOutputWlsImageProperties`|Shell command to display the base64 encoded string of the model properties.{{< line_break >}}Sample value:{{< line_break >}}`echo -e IyBDF...PTUK \| base64 -d > model.properties` | From da2ae40e9fc8cac0c0e471278830babc834b5b1b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 21 Jun 2024 15:19:23 -0400 Subject: [PATCH 090/356] Correct sed usage in waitForDomain.sh --- kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh b/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh index 9df689caf81..e3b3f722d3f 100755 --- a/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh +++ b/kubernetes/samples/scripts/domain-lifecycle/waitForDomain.sh @@ -308,7 +308,7 @@ getDomainInfo() { getDomainValue domain_info_observed_generation ".status.observedGeneration" getDomainValue domain_info_condition_completed ".status.conditions[?(@.type==\"Completed\")].status" # "True" when complete - domain_info_clusters=$( echo "$domain_info_clusters" | sed 's/"name"//g' | tr -d '[]{}:' | sortlist | sed 's/,/ /') # convert to sorted space separated list + domain_info_clusters=$( echo "$domain_info_clusters" | sed 's/"name"//g' | tr -d '[]{}:' | sortlist | sed 's/,/ /g') # convert to sorted space separated list # gather observed and goal generation for each cluster: cl_info_observed_generations="" From a2ea864d71a810c2e256036889a4ca7c1de8310d Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 24 Jun 2024 18:16:38 +0000 Subject: [PATCH 091/356] Merge branch 'owls-118993-doc-mii' into 'main' Owls 118993 doc mii See merge request weblogic-cloud/weblogic-kubernetes-operator!4735 (cherry picked from commit 4b5917a20f900620ff4a2ed27ced7d570e2a86dd) 866b2405 update details for mii 0a28405d update wordings 6dc46e66 incorporate edits --- .../model-in-image/overview.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/documentation/site/content/managing-domains/model-in-image/overview.md b/documentation/site/content/managing-domains/model-in-image/overview.md index 6d6ce401b04..40fe61aaa3f 100644 --- a/documentation/site/content/managing-domains/model-in-image/overview.md +++ b/documentation/site/content/managing-domains/model-in-image/overview.md @@ -43,14 +43,21 @@ The WDT model format is fully described in the open source, When you deploy a Model in Image domain resource YAML file: - The operator will run a Kubernetes Job called the 'introspector job' that: - - Merges your WDT models. - - Runs WDT tooling to generate a domain home. - - Packages the domain home and passes it to the operator. + - For an [Auxiliary Image]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) deployment, an init container is used to copy and set up the WDT installer in the main container, and all the WDT models are also copied to the main container. + - Sets up the call parameters for WDT to create the domain. The ordering of the models follow the pattern [Model files naming and ordering]({{< relref "/managing-domains/model-in-image/model-files#model-file-naming-and-loading-order" >}}). + - Runs WDT tooling to generate a domain home using the parameters from the previous step. + - Encrypts the domain salt key `SerializedSystemIni.dat`. + - Packages the domain home and passes it to the operator. The packaged domain has two parts. The first part `primordial domain` contains the basic configuration including the encrypted salt key. The second part `domain config` contains the rest of the configuration `config/**/*.xml`. These files are compressed but do not contain any applications, libraries, key stores, and such, because they can be restored from the WDT archives. - After the introspector job completes: - - The operator creates one or more ConfigMaps following the pattern `DOMAIN_UID-weblogic-domain-introspect-cm***`. - - The operator subsequently boots your domain's WebLogic Server pods. - - The pods will obtain their domain home from the ConfigMap. + - The operator creates one or more ConfigMaps following the pattern `DOMAIN_UID-weblogic-domain-introspect-cm***`. These ConfigMaps contain the packaged domains from the introspector job and other information for starting the domain. + + - After completion of the introspector job, the operator will start the domain: + - For an [Auxiliary Image]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) deployment, an init container is used to copy and set up the WDT installer in the main container, and all the WDT models are also copied to the main container first. + - Restore the packaged domains in the server pod. + - Restore applications, libraries, key stores, and such, from the WDT archives. + - Decrypt the domain salt key. + - Start the domain. ### Runtime updates From 8c8e90092a22a931ab32d9164e292ca1ef335c1f Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Mon, 24 Jun 2024 18:35:15 +0000 Subject: [PATCH 092/356] fix umask in wko scripts --- operator/src/main/resources/scripts/utils_base.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/operator/src/main/resources/scripts/utils_base.sh b/operator/src/main/resources/scripts/utils_base.sh index 2af9e1834b6..ceeb059524f 100644 --- a/operator/src/main/resources/scripts/utils_base.sh +++ b/operator/src/main/resources/scripts/utils_base.sh @@ -47,6 +47,8 @@ # # Set TRACE_INCLUDE_FILE env var to false to suppress file name and line number. # +umask 027 + export AUXILIARY_IMAGE_COMMAND_LOGS_DIR="${AUXILIARY_IMAGE_COMMAND_LOGS_DIR:-compatibilityModeInitContainerLogs}" trace() { From 0e15e4212f07271e8856df9797e4e6a1d021e954 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Mon, 1 Jul 2024 22:17:45 +0000 Subject: [PATCH 093/356] Temporarily stop nightly OKD run --- Jenkinsfile.okd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.okd b/Jenkinsfile.okd index 9879274d510..8c97199a554 100644 --- a/Jenkinsfile.okd +++ b/Jenkinsfile.okd @@ -15,7 +15,7 @@ pipeline { } triggers { // timer trigger for "nightly build" - parameterizedCron(env.JOB_NAME == 'okd-dev' ? + parameterizedCron(env.JOB_NAME == 'wko-release42-nightly-okd' ? CRON_SETTINGS : '') } From eec572104335ac92c2c5ad0052ca4383ae811048 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 1 Jul 2024 22:21:49 +0000 Subject: [PATCH 094/356] Merge branch 'fix-startnm-script-error' into 'main' Correct startNodeManager.sh NODEMGR_JAVA_OPTIONS error See merge request weblogic-cloud/weblogic-kubernetes-operator!4743 (cherry picked from commit 771d76b5889bdad5949bc7f70d1b4a82a929d405) 658f0ca8 fix script error when NODEMGR_JAVA_OPTIONS is set with multiple parameters --- operator/src/main/resources/scripts/startNodeManager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/startNodeManager.sh b/operator/src/main/resources/scripts/startNodeManager.sh index 6d5af9c7a81..4c169255a9a 100644 --- a/operator/src/main/resources/scripts/startNodeManager.sh +++ b/operator/src/main/resources/scripts/startNodeManager.sh @@ -322,7 +322,7 @@ export NODEMGR_HOME="${NODEMGR_HOME?}" export DOMAIN_HOME="${DOMAIN_HOME?}" # Apply JAVA_OPTIONS to Node Manager if NODEMGR_JAVA_OPTIONS not specified -if [ -z ${NODEMGR_JAVA_OPTIONS} ]; then +if [ -z "${NODEMGR_JAVA_OPTIONS}" ]; then NODEMGR_JAVA_OPTIONS="${JAVA_OPTIONS}" fi From 224c0ce204336dc04b35541f08081c0f3f232438 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 1 Jul 2024 18:29:31 -0400 Subject: [PATCH 095/356] Sample YAML create cluster before domain --- .../samples/quick-start/domain-resource.yaml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/kubernetes/samples/quick-start/domain-resource.yaml b/kubernetes/samples/quick-start/domain-resource.yaml index a79de699838..ca6e863ee80 100644 --- a/kubernetes/samples/quick-start/domain-resource.yaml +++ b/kubernetes/samples/quick-start/domain-resource.yaml @@ -1,6 +1,22 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +apiVersion: "weblogic.oracle/v1" +kind: Cluster +metadata: + name: sample-domain1-cluster-1 + # Update this with the namespace your domain will run in: + namespace: sample-domain1-ns + labels: + # Update this with the `domainUID` of your domain: + weblogic.domainUID: sample-domain1 + +spec: + replicas: 2 + clusterName: cluster-1 + +--- + apiVersion: "weblogic.oracle/v9" kind: Domain metadata: @@ -128,20 +144,4 @@ spec: # Secrets that are referenced by model yaml macros # (the model yaml in the optional configMap or in the image) #secrets: - #- sample-domain1-datasource-secret - ---- - -apiVersion: "weblogic.oracle/v1" -kind: Cluster -metadata: - name: sample-domain1-cluster-1 - # Update this with the namespace your domain will run in: - namespace: sample-domain1-ns - labels: - # Update this with the `domainUID` of your domain: - weblogic.domainUID: sample-domain1 - -spec: - replicas: 2 - clusterName: cluster-1 + #- sample-domain1-datasource-secret \ No newline at end of file From 1aeaa4ee1fb0e0518f2867f806757655d0af2a03 Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Tue, 2 Jul 2024 16:28:41 +0000 Subject: [PATCH 096/356] [OKE] [backport] internal OKE conversion for ItConfigDistributionStrategy & ItIntrospectVersion on main into release/4.2 --- .../ItConfigDistributionStrategy.java | 74 +++++++++++++++++-- .../kubernetes/ItIntrospectVersion.java | 4 +- .../kubernetes/utils/LoadBalancerUtils.java | 61 +++++++++++++++ 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index c1e3fc221fd..2f637aad4a5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -42,6 +42,8 @@ import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.NginxParams; +import oracle.weblogic.kubernetes.actions.impl.Service; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; @@ -66,6 +68,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_12213; @@ -93,6 +96,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; @@ -105,6 +109,8 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createNginxIngressPathRoutingRules; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; import static oracle.weblogic.kubernetes.utils.MySQLDBUtils.createMySQLDB; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; @@ -133,13 +139,15 @@ @DisplayName("Verify the overrideDistributionStrategy applies the overrides accordingly to the value set") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-sequential") +@Tag("oke-gate") @IntegrationTest @Tag("olcne-mrg") class ItConfigDistributionStrategy { private static String opNamespace = null; private static String domainNamespace = null; + private static String nginxNamespace = null; + private static NginxParams nginxHelmParams = null; final String domainUid = "mydomain"; final String clusterName = "mycluster"; @@ -155,6 +163,7 @@ class ItConfigDistributionStrategy { final String wlSecretName = "weblogic-credentials"; final String managedServerPodNamePrefix = domainUid + "-" + managedServerNameBase; int replicaCount = 2; + static String hostAndPort; static Path clusterViewAppPath; String overridecm = "configoverride-cm"; @@ -166,12 +175,13 @@ class ItConfigDistributionStrategy { static String dsUrl2; static String mysql1SvcEndpoint = null; static String mysql2SvcEndpoint = null; + private static String ingressIP = null; String dsName0 = "JdbcTestDataSource-0"; String dsName1 = "JdbcTestDataSource-1"; String dsSecret = domainUid.concat("-mysql-secret"); String adminSvcExtHost = null; - static String hostHeader; + static String hostHeader; private static LoggingFacade logger = null; @@ -187,7 +197,7 @@ class ItConfigDistributionStrategy { * @param namespaces injected by JUnit */ @BeforeAll - public void initAll(@Namespaces(2) List namespaces) throws ApiException, IOException { + public void initAll(@Namespaces(3) List namespaces) throws ApiException, IOException { logger = getLogger(); logger.info("Assign a unique namespace for operator"); @@ -196,10 +206,20 @@ public void initAll(@Namespaces(2) List namespaces) throws ApiException, logger.info("Assign a unique namespace for domain namspace"); assertNotNull(namespaces.get(1), "Namespace is null"); domainNamespace = namespaces.get(1); + assertNotNull(namespaces.get(2), "Namespace is null"); + nginxNamespace = namespaces.get(2); // install operator and verify its running in ready state installAndVerifyOperator(opNamespace, domainNamespace); + ingressIP = K8S_NODEPORT_HOST; + if (OKE_CLUSTER_PRIVATEIP) { + // install and verify NGINX + nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + ingressIP = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null + ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST; + } // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); @@ -232,6 +252,14 @@ public void initAll(@Namespaces(2) List namespaces) throws ApiException, //create and start WebLogic domain createDomain(); + if (OKE_CLUSTER_PRIVATEIP) { + String ingressClassName = nginxHelmParams.getIngressClassName(); + String serviceName = domainUid + "-admin-server"; + final int ADMIN_SERVER_PORT = 7001; + String hostAndPort = getHostAndPortOKE(); + createNginxIngressPathRoutingRules(domainNamespace, ingressClassName, + serviceName, ADMIN_SERVER_PORT, hostAndPort); + } // Expose the admin service external node port as a route for OKD adminSvcExtHost = createRouteForOKD(getExternalServicePodName(adminServerPodName), domainNamespace); @@ -286,6 +314,9 @@ public void afterEach() { headers.put("host", hostHeader); } boolean ipv6 = K8S_NODEPORT_HOST.contains(":"); + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = ingressIP; + } String baseUri = "http://" + hostAndPort + "/clusterview/"; String serverListUri = "ClusterViewServlet?user=" + ADMIN_USERNAME_DEFAULT + "&password=" + ADMIN_PASSWORD_DEFAULT + "&ipv6=" + ipv6; @@ -688,6 +719,9 @@ private Callable configUpdated(String maxMessageSize) { headers = new HashMap<>(); headers.put("host", hostHeader); } + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = ingressIP; + } String url = "http://" + hostAndPort + appURI; HttpResponse response = OracleHttpClient.get(url, headers, true); assertEquals(200, response.statusCode(), "Status code not equals to 200"); @@ -714,6 +748,9 @@ private void verifyConfigXMLOverride(boolean configUpdated) { headers = new HashMap<>(); headers.put("host", hostHeader); } + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = getHostAndPortOKE(); + } String baseUri = "http://" + hostAndPort + "/clusterview/"; HttpResponse response = OracleHttpClient.get(baseUri + configUri, headers, true); if (response.statusCode() != 200) { @@ -746,6 +783,9 @@ private void verifyResourceJDBC0Override(boolean configUpdated) { headers = new HashMap<>(); headers.put("host", hostHeader); } + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = getHostAndPortOKE(); + } logger.info("hostAndPort = {0} ", hostAndPort); String baseUri = "http://" + hostAndPort + "/clusterview/ConfigServlet?"; //verify datasource attributes of JdbcTestDataSource-0 @@ -786,7 +826,7 @@ private void verifyResourceJDBC0Override(boolean configUpdated) { testDatasource(appURI); } } - + private void testDatasource(String appURI) { int port = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); testUntil( @@ -799,6 +839,9 @@ private void testDatasource(String appURI) { headers = new HashMap<>(); headers.put("host", hostHeader); } + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = getHostAndPortOKE(); + } logger.info("hostAndPort = {0} ", hostAndPort); String baseUri = "http://" + hostAndPort + "/clusterview/ConfigServlet?"; @@ -837,11 +880,14 @@ private void verifyResourceJDBC1Override(boolean configUpdated) { headers = new HashMap<>(); headers.put("host", hostHeader); } + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = getHostAndPortOKE(); + } String baseUri = "http://" + hostAndPort + "/clusterview/ConfigServlet?"; //verify datasource attributes of JdbcTestDataSource-0 String appURI = "resTest=true&resName=" + dsName1; - String dsOverrideTestUrl = baseUri + appURI; + String dsOverrideTestUrl = baseUri + appURI; HttpResponse response = OracleHttpClient.get(dsOverrideTestUrl, headers, true); if (response.statusCode() != 200) { logger.info("Response code is not 200 retrying..."); @@ -891,6 +937,9 @@ private void verifyResourceJDBC1Override(boolean configUpdated) { headers = new HashMap<>(); headers.put("host", hostHeader); } + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = getHostAndPortOKE(); + } String baseUri = "http://" + hostAndPort + "/clusterview/ConfigServlet?"; String appURI = "dsTest=true&dsName=" + dsName1 + "&" + "serverName=" + managedServerNameBase + i; String dsConnectionPoolTestUrl = baseUri + appURI; @@ -934,7 +983,7 @@ private void verifyIntrospectorRuns() { //create a standard WebLogic domain. private void createDomain() { - + String uniquePath = "/shared/" + domainNamespace + "/domains"; // create WebLogic domain credential secret @@ -959,7 +1008,7 @@ private void createDomain() { p.setProperty("admin_server_port", "7001"); p.setProperty("admin_username", ADMIN_USERNAME_DEFAULT); p.setProperty("admin_password", ADMIN_PASSWORD_DEFAULT); - p.setProperty("admin_t3_public_address", K8S_NODEPORT_HOST); + p.setProperty("admin_t3_public_address", ingressIP); p.setProperty("admin_t3_channel_port", Integer.toString(t3ChannelPort)); p.setProperty("number_of_ms", "2"); p.setProperty("managed_server_name_base", managedServerNameBase); @@ -1166,7 +1215,7 @@ private void createJdbcDataSource(String dsName, String user, String password, S * @param namespace name of the domain namespace in which the job is created */ private void createDomainOnPVUsingWlst(Path wlstScriptFile, Path domainPropertiesFile, - String pvName, String pvcName, String namespace) { + String pvName, String pvcName, String namespace) { logger.info("Preparing to run create domain job using WLST"); List domainScriptFiles = new ArrayList<>(); @@ -1290,4 +1339,13 @@ private void createFileInPod(String podName, String namespace, String password) assertEquals(0, result.exitValue(), "mysql execution fails"); } + private static String getHostAndPortOKE() { + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + int nginxNodePort = assertDoesNotThrow(() -> Service.getServiceNodePort(nginxNamespace, nginxServiceName, "http"), + "Getting Nginx loadbalancer service node port failed"); + + hostAndPort = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null + ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST + ":" + nginxNodePort; + return hostAndPort; + } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index e14f726b5d6..9103595b165 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -172,7 +172,7 @@ @Tag("olcne-srg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-sequential1") +@Tag("oke-gate") @Tag("oke-arm") class ItIntrospectVersion { @@ -1369,7 +1369,6 @@ private static void verifyMemberHealth(String adminServerPodName, List m } else { // In non-internal OKE env, verifyMemberHealth using adminSvcExtHost by sending HTTP request from local VM - // TEST, HERE String extSvcPodName = getExternalServicePodName(adminServerPodName); logger.info("**** adminServerPodName={0}", adminServerPodName); logger.info("**** extSvcPodName={0}", extSvcPodName); @@ -1588,5 +1587,4 @@ private void updateIngressBackendServicePort(int newAdminPort) throws ApiExcepti fail("Ingress is null, failed to update ingress"); } } - } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index d595e10b5c5..ee717d91360 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -15,10 +15,15 @@ import io.kubernetes.client.custom.IntOrString; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1HTTPIngressPath; +import io.kubernetes.client.openapi.models.V1HTTPIngressRuleValue; +import io.kubernetes.client.openapi.models.V1IngressBackend; import io.kubernetes.client.openapi.models.V1IngressRule; +import io.kubernetes.client.openapi.models.V1IngressServiceBackend; import io.kubernetes.client.openapi.models.V1IngressTLS; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Service; +import io.kubernetes.client.openapi.models.V1ServiceBackendPort; import io.kubernetes.client.openapi.models.V1ServicePort; import io.kubernetes.client.openapi.models.V1ServiceSpec; import oracle.weblogic.kubernetes.TestConstants; @@ -460,6 +465,62 @@ public static List createIngressForDomainAndVerify(String domainUid, return ingressHostList; } + /** + * Creates Nginx Ingress Path Routing for service. + * @param domainNamespace - domain namespace + * @param ingressClassName - class name + * @param serviceName - service name + * @param servicePort -service port + * @param hostAndPort - host and port for url + */ + public static void createNginxIngressPathRoutingRules(String domainNamespace, + String ingressClassName, + String serviceName, + int servicePort, + String hostAndPort) { + // create an ingress in domain namespace + String ingressName = domainNamespace + "-nginx-path-routing"; + + // create ingress rules for two domains + List ingressRules = new ArrayList<>(); + List httpIngressPaths = new ArrayList<>(); + + V1HTTPIngressPath httpIngressPath = new V1HTTPIngressPath() + .path("/") + .pathType("Prefix") + .backend(new V1IngressBackend() + .service(new V1IngressServiceBackend() + .name(serviceName) + .port(new V1ServiceBackendPort() + .number(servicePort))) + ); + httpIngressPaths.add(httpIngressPath); + + V1IngressRule ingressRule = new V1IngressRule() + .host("") + .http(new V1HTTPIngressRuleValue() + .paths(httpIngressPaths)); + + ingressRules.add(ingressRule); + + createIngressAndRetryIfFail(60, false, ingressName, domainNamespace, null, ingressClassName, ingressRules, null); + + // check the ingress was found in the domain namespace + assertThat(assertDoesNotThrow(() -> listIngresses(domainNamespace))) + .as(String.format("Test ingress %s was found in namespace %s", ingressName, domainNamespace)) + .withFailMessage(String.format("Ingress %s was not found in namespace %s", ingressName, domainNamespace)) + .contains(ingressName); + LoggingFacade logger = getLogger(); + logger.info("ingress {0} was created in namespace {1}", ingressName, domainNamespace); + + // check the ingress is ready to route the app to the server pod + String curlCmd = "curl -g --silent --show-error --noproxy '*' http://" + hostAndPort + + "/weblogic/ready --write-out %{http_code} -o /dev/null"; + + logger.info("Executing curl command {0}", curlCmd); + assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); + } + /** * Create an ingress for the domain with domainUid in the specified namespace. * From e7362f941d4c0cefbd75ee40d62380ebc99c1439 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Wed, 3 Jul 2024 18:46:50 +0000 Subject: [PATCH 097/356] Change tests to use domain creation images for Domain In PV usecases --- Jenkinsfile.podman | 2 +- .../ItConfigDistributionStrategy.java | 187 +++++-------- .../kubernetes/ItIntrospectVersion.java | 173 ++++++------ .../kubernetes/ItKubernetesDomainEvents.java | 256 +++++++++--------- .../kubernetes/ItSystemResOverrides.java | 143 ++++++---- .../weblogic/kubernetes/ItT3Channel.java | 177 ++++++------ .../kubernetes/actions/impl/Cluster.java | 2 +- .../introspect_version_script.py | 2 +- .../wdt-models/model-dci-introspect.yaml | 33 +++ .../wdt-models/sitconfig-dci-model.yaml | 63 +++++ 10 files changed, 555 insertions(+), 483 deletions(-) create mode 100644 integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml create mode 100644 integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 92b6b3b29e2..a6681d9a2b2 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -25,7 +25,7 @@ CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.17''' pipeline { - agent { label 'large-ol9' } + agent { label 'large-ol9u4' } options { timeout(time: 800, unit: 'MINUTES') } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index 2f637aad4a5..99d482861f6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -13,7 +13,7 @@ import java.nio.file.StandardCopyOption; import java.time.OffsetDateTime; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -22,29 +22,26 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1ObjectMeta; -import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; import io.kubernetes.client.openapi.models.V1Pod; import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Service; -import io.kubernetes.client.openapi.models.V1Volume; -import io.kubernetes.client.openapi.models.V1VolumeMount; -import oracle.weblogic.domain.AdminServer; -import oracle.weblogic.domain.AdminService; -import oracle.weblogic.domain.Channel; -import oracle.weblogic.domain.ClusterResource; +import io.kubernetes.client.util.Yaml; import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; import oracle.weblogic.domain.DomainResource; -import oracle.weblogic.domain.DomainSpec; -import oracle.weblogic.domain.ServerPod; import oracle.weblogic.kubernetes.actions.impl.NginxParams; import oracle.weblogic.kubernetes.actions.impl.Service; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -64,16 +61,18 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; -import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; -import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_12213; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.createSecret; @@ -89,9 +88,8 @@ import static oracle.weblogic.kubernetes.actions.impl.Domain.patchDomainCustomResource; import static oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes.listConfigMaps; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podStateNotChanged; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.BuildApplication.buildApplication; -import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; -import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; @@ -104,8 +102,10 @@ import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapFromFiles; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; @@ -114,14 +114,11 @@ import static oracle.weblogic.kubernetes.utils.MySQLDBUtils.createMySQLDB; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVC; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDoesNotExist; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.getPodCreationTime; -import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static oracle.weblogic.kubernetes.utils.WLSTUtils.executeWLSTScript; @@ -155,8 +152,7 @@ class ItConfigDistributionStrategy { final String adminServerName = "admin-server"; final int adminPort = 7001; final String adminServerPodName = domainUid + "-" + adminServerName; - final String managedServerNameBase = "ms-"; - final int managedServerPort = 8001; + final String managedServerNameBase = "managed-server"; int t3ChannelPort; final String pvName = getUniqueName(domainUid + "-pv-"); final String pvcName = getUniqueName(domainUid + "-pvc-"); @@ -169,12 +165,8 @@ class ItConfigDistributionStrategy { String overridecm = "configoverride-cm"; LinkedHashMap podTimestamps; - static int mysqlDBPort1; - static int mysqlDBPort2; static String dsUrl1; static String dsUrl2; - static String mysql1SvcEndpoint = null; - static String mysql2SvcEndpoint = null; private static String ingressIP = null; String dsName0 = "JdbcTestDataSource-0"; @@ -983,104 +975,67 @@ private void verifyIntrospectorRuns() { //create a standard WebLogic domain. private void createDomain() { - - String uniquePath = "/shared/" + domainNamespace + "/domains"; + String uniqueDomainHome = "/shared/" + domainNamespace + "/domains"; // create WebLogic domain credential secret createSecretWithUsernamePassword(wlSecretName, domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); - - createPV(pvName, domainUid, this.getClass().getSimpleName()); - createPVC(pvName, pvcName, domainUid, domainNamespace); - + final String wlsModelFilePrefix = "model-dci-introspect"; + final String wlsModelFile = wlsModelFilePrefix + ".yaml"; t3ChannelPort = getNextFreePort(); - - // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), - "Failed to create domain properties file"); - Properties p = new Properties(); - p.setProperty("domain_path", uniquePath); - p.setProperty("domain_name", domainUid); - p.setProperty("cluster_name", clusterName); - p.setProperty("admin_server_name", adminServerName); - p.setProperty("managed_server_port", Integer.toString(managedServerPort)); - p.setProperty("admin_server_port", "7001"); - p.setProperty("admin_username", ADMIN_USERNAME_DEFAULT); - p.setProperty("admin_password", ADMIN_PASSWORD_DEFAULT); - p.setProperty("admin_t3_public_address", ingressIP); - p.setProperty("admin_t3_channel_port", Integer.toString(t3ChannelPort)); - p.setProperty("number_of_ms", "2"); - p.setProperty("managed_server_name_base", managedServerNameBase); - p.setProperty("domain_logs", uniquePath + "/logs"); - p.setProperty("production_mode_enabled", "true"); - assertDoesNotThrow(() - -> p.store(new FileOutputStream(domainPropertiesFile), "domain properties file"), - "Failed to write domain properties file"); - - // WLST script for creating domain - Path wlstScript = Paths.get(RESOURCE_DIR, "python-scripts", "wlst-create-domain-onpv.py"); - - // create configmap and domain on persistent volume using the WLST script and property file - createDomainOnPVUsingWlst(wlstScript, domainPropertiesFile.toPath(), - pvName, pvcName, domainNamespace); - - // create a domain custom resource configuration object + File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + K8S_NODEPORT_HOST, t3ChannelPort); + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "configdist-domain-on-pv-image"; + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) + .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + + // create a domain resource logger.info("Creating domain custom resource"); - DomainResource domain = new DomainResource() - .apiVersion(DOMAIN_API_VERSION) - .kind("Domain") - .metadata(new V1ObjectMeta() - .name(domainUid) - .namespace(domainNamespace)) - .spec(new DomainSpec() - .configuration(new Configuration() - .overrideDistributionStrategy("Dynamic") - .introspectorJobActiveDeadlineSeconds(300L)) - .domainUid(domainUid) - .domainHome(uniquePath + "/" + domainUid) // point to domain home in pv - .domainHomeSourceType("PersistentVolume") // set the domain home source type as pv - .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) - .imagePullPolicy(IMAGE_PULL_POLICY) - .imagePullSecrets(Arrays.asList( - new V1LocalObjectReference() - .name(BASE_IMAGES_REPO_SECRET_NAME))) // this secret is used only in non-kind cluster - .webLogicCredentialsSecret(new V1LocalObjectReference() - .name(wlSecretName)) - .includeServerOutInPodLog(true) - .logHomeEnabled(Boolean.TRUE) - .logHome(uniquePath + "/logs/" + domainUid) - .dataHome("") - .serverStartPolicy("IfNeeded") - .serverPod(new ServerPod() //serverpod - .addEnvItem(new V1EnvVar() - .name("JAVA_OPTIONS") - .value("-Dweblogic.debug.DebugSituationalConfig=true " - + "-Dweblogic.debug.DebugSituationalConfigDumpXml=true ")) - .addEnvItem(new V1EnvVar() - .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom ")) - .addVolumesItem(new V1Volume() - .name(pvName) - .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() - .claimName(pvcName))) - .addVolumeMountsItem(new V1VolumeMount() - .mountPath("/shared") - .name(pvName))) - .adminServer(new AdminServer() //admin server - .adminService(new AdminService() - .addChannelsItem(new Channel() - .channelName("default") - .nodePort(0))))); - setPodAntiAffinity(domain); - - // create cluster object - ClusterResource cluster = createClusterResource(clusterResName, - clusterName, domainNamespace, replicaCount); - logger.info("Creating cluster resource {0} in namespace {1}", clusterResName, domainNamespace); - createClusterAndVerify(cluster); - // set cluster references - domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterResName)); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); + + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + final String storageClassName = "weblogic-domain-storage-class"; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + ItIntrospectVersion.class.getName()); + } + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); + configuration.overrideDistributionStrategy("Dynamic"); + + DomainResource domain = createDomainResourceOnPv(domainUid, + domainNamespace, + wlSecretName, + clusterName, + pvName, + pvcName, + new String[]{BASE_IMAGES_REPO_SECRET_NAME}, + uniqueDomainHome, + 2, + t3ChannelPort, + configuration); + domain.spec().serverPod().addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.debug.DebugSituationalConfig=true " + + "-Dweblogic.debug.DebugSituationalConfigDumpXml=true ")); + logger.info(Yaml.dump(domain)); // verify the domain custom resource is created createDomainAndVerify(domain, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index 9103595b165..c053ba82497 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Properties; +import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1Container; @@ -27,17 +28,18 @@ import io.kubernetes.client.openapi.models.V1Ingress; import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1ObjectMeta; -import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; import io.kubernetes.client.openapi.models.V1Pod; import io.kubernetes.client.openapi.models.V1ServiceBackendPort; -import io.kubernetes.client.openapi.models.V1Volume; -import io.kubernetes.client.openapi.models.V1VolumeMount; import oracle.weblogic.domain.AdminServer; import oracle.weblogic.domain.AdminService; import oracle.weblogic.domain.Channel; import oracle.weblogic.domain.ClusterResource; import oracle.weblogic.domain.ClusterStatus; import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.Model; @@ -46,6 +48,7 @@ import oracle.weblogic.kubernetes.actions.impl.Ingress; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -65,6 +68,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_PATCH; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_PATCH; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_REPO; @@ -87,6 +91,7 @@ import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.ITTESTS_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deleteSecret; @@ -107,6 +112,7 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.podStateNotChanged; import static oracle.weblogic.kubernetes.assertions.TestAssertions.verifyRollingRestartOccurred; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.verifyPodsNotRolled; @@ -128,7 +134,9 @@ import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeExists; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeHasExpectedStatus; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createMiiImageAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; @@ -141,8 +149,6 @@ import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResource; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVC; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDoesNotExist; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; @@ -151,7 +157,6 @@ import static oracle.weblogic.kubernetes.utils.PodUtils.getPodsWithTimeStamps; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; -import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretsForImageRepos; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static oracle.weblogic.kubernetes.utils.WLSTUtils.executeWLSTScript; import static org.apache.commons.io.FileUtils.copyDirectory; @@ -202,6 +207,7 @@ class ItIntrospectVersion { private static final String pvName = getUniqueName(domainUid + "-pv-"); private static final String pvcName = getUniqueName(domainUid + "-pvc-"); + private static final String storageClassName = "weblogic-domain-storage-class"; private static final String wlSecretName = "weblogic-credentials"; private static String wlsUserName = ADMIN_USERNAME_DEFAULT; @@ -1136,98 +1142,59 @@ void testIntrospectorMakeright() { } private static void createDomain() { - String uniquePath = "/shared/" + introDomainNamespace + "/domains"; + String uniqueDomainHome = "/shared/" + introDomainNamespace + "/domains/"; // create WebLogic domain credential secret createSecretWithUsernamePassword(wlSecretName, introDomainNamespace, wlsUserName, wlsPassword); - createPV(pvName, domainUid, ItIntrospectVersion.class.getSimpleName()); - createPVC(pvName, pvcName, domainUid, introDomainNamespace); - - // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), - "Failed to create domain properties file"); - Properties p = new Properties(); - p.setProperty("domain_path", uniquePath); - p.setProperty("domain_name", domainUid); - p.setProperty("cluster_name", cluster1Name); - p.setProperty("admin_server_name", adminServerName); - p.setProperty("managed_server_port", Integer.toString(managedServerPort)); - p.setProperty("admin_server_port", "7001"); - p.setProperty("admin_username", wlsUserName); - p.setProperty("admin_password", wlsPassword); - p.setProperty("admin_t3_public_address", K8S_NODEPORT_HOST); - p.setProperty("admin_t3_channel_port", Integer.toString(t3ChannelPort)); - p.setProperty("number_of_ms", "2"); // maximum number of servers in cluster - p.setProperty("managed_server_name_base", cluster1ManagedServerNameBase); - p.setProperty("domain_logs", uniquePath + "/logs/" + domainUid); - p.setProperty("production_mode_enabled", "true"); - assertDoesNotThrow(() -> - p.store(new FileOutputStream(domainPropertiesFile), "domain properties file"), - "Failed to write domain properties file"); - - // WLST script for creating domain - Path wlstScript = Paths.get(RESOURCE_DIR, "python-scripts", "wlst-create-domain-onpv.py"); - - // create configmap and domain on persistent volume using the WLST script and property file - createDomainOnPVUsingWlst(wlstScript, domainPropertiesFile.toPath(), - pvName, pvcName, introDomainNamespace); - - // create cluster object - String clusterResName = domainUid + "-" + cluster1Name; - ClusterResource cluster = createClusterResource(clusterResName, - cluster1Name, introDomainNamespace, cluster1ReplicaCount); - - logger.info("Creating cluster resource {0} in namespace {1}",clusterResName, introDomainNamespace); - createClusterAndVerify(cluster); - - // create a domain custom resource configuration object + final String wlsModelFilePrefix = "model-dci-introspect"; + final String wlsModelFile = wlsModelFilePrefix + ".yaml"; + File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); + + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "introspect-domain-on-pv-image"; + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) + .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + + // create a domain resource logger.info("Creating domain custom resource"); - DomainResource domain = new DomainResource() - .apiVersion(DOMAIN_API_VERSION) - .kind("Domain") - .metadata(new V1ObjectMeta() - .name(domainUid) - .namespace(introDomainNamespace)) - .spec(new DomainSpec() - .domainUid(domainUid) - .domainHome(uniquePath + "/" + domainUid) // point to domain home in pv - .domainHomeSourceType("PersistentVolume") // set the domain home source type as pv - .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) - .imagePullPolicy(IMAGE_PULL_POLICY) - .webLogicCredentialsSecret(new V1LocalObjectReference() - .name(wlSecretName)) - .includeServerOutInPodLog(true) - .logHomeEnabled(Boolean.TRUE) - .logHome(uniquePath + "/logs/" + domainUid) - .dataHome("") - .serverStartPolicy("IfNeeded") - .serverPod(new ServerPod() //serverpod - .addEnvItem(new V1EnvVar() - .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom ")) - .addVolumesItem(new V1Volume() - .name(pvName) - .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() - .claimName(pvcName))) - .addVolumeMountsItem(new V1VolumeMount() - .mountPath("/shared") - .name(pvName))) - .adminServer(new AdminServer() //admin server - .adminService(new AdminService() - .addChannelsItem(new Channel() - .channelName("default") - .nodePort(getNextFreePort()))))); - - // create secrets - List secrets = new ArrayList<>(); - for (String secret : createSecretsForImageRepos(introDomainNamespace)) { - secrets.add(new V1LocalObjectReference().name(secret)); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); + + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + ItIntrospectVersion.class.getSimpleName()); } - domain.spec().setImagePullSecrets(secrets); - // set cluster references - domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterResName)); + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); + + DomainResource domain = createDomainResourceOnPv(domainUid, + introDomainNamespace, + wlSecretName, + cluster1Name, + pvName, + pvcName, + new String[]{BASE_IMAGES_REPO_SECRET_NAME}, + uniqueDomainHome, + cluster1ReplicaCount, + t3ChannelPort, + configuration); setPodAntiAffinity(domain); // verify the domain custom resource is created @@ -1587,4 +1554,26 @@ private void updateIngressBackendServicePort(int newAdminPort) throws ApiExcepti fail("Ingress is null, failed to update ingress"); } } + + public static File createWdtPropertyFile(String wlsModelFilePrefix, String nodePortHost, int t3Port) { + + // create property file used with domain model file + Properties p = new Properties(); + p.setProperty("WebLogicAdminUserName", ADMIN_USERNAME_DEFAULT); + p.setProperty("WebLogicAdminPassword", ADMIN_PASSWORD_DEFAULT); + p.setProperty("K8S_NODEPORT_HOST", nodePortHost); + p.setProperty("T3_CHANNEL_PORT", Integer.toString(t3Port)); + + // create a model property file + File domainPropertiesFile = assertDoesNotThrow(() -> + File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), + "Failed to create WLS model properties file"); + + // create the property file + assertDoesNotThrow(() -> + p.store(new FileOutputStream(domainPropertiesFile), "WLS properties file"), + "Failed to write WLS properties file"); + + return domainPropertiesFile; + } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index 232a64c979c..baa929582ab 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -9,26 +9,37 @@ import java.nio.file.Paths; import java.time.OffsetDateTime; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.function.UnaryOperator; +import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; -import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; import io.kubernetes.client.openapi.models.V1Volume; import io.kubernetes.client.openapi.models.V1VolumeMount; +import oracle.weblogic.domain.AdminServer; +import oracle.weblogic.domain.AdminService; import oracle.weblogic.domain.ClusterList; +import oracle.weblogic.domain.ClusterResource; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.assertions.impl.Cluster; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.DomainUtils; import org.junit.jupiter.api.AfterAll; @@ -40,13 +51,20 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_ROLLING_TYPE; +import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS; +import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deleteClusterCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.deleteDomainCustomResource; @@ -61,9 +79,9 @@ import static oracle.weblogic.kubernetes.actions.impl.Cluster.listClusterCustomResources; import static oracle.weblogic.kubernetes.actions.impl.Domain.patchDomainCustomResource; import static oracle.weblogic.kubernetes.assertions.TestAssertions.verifyRollingRestartOccurred; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; -import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; import static oracle.weblogic.kubernetes.utils.ClusterUtils.removeReplicasSettingAndVerify; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; @@ -71,13 +89,12 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; -import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapForDomainCreation; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.deleteDomainResource; import static oracle.weblogic.kubernetes.utils.DomainUtils.removeClusterInDomainResource; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; -import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; import static oracle.weblogic.kubernetes.utils.K8sEvents.ABORTED_ERROR; import static oracle.weblogic.kubernetes.utils.K8sEvents.CLUSTER_AVAILABLE; @@ -109,7 +126,6 @@ import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getPodsWithTimeStamps; -import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static oracle.weblogic.kubernetes.utils.WLSTUtils.executeWLSTScript; @@ -150,9 +166,8 @@ class ItKubernetesDomainEvents { static final String adminServerName = "admin-server"; static final String domainUid = "k8seventsdomain"; static final String adminServerPodName = domainUid + "-" + adminServerName; - static final String managedServerNameBase = "ms-"; + static final String managedServerNameBase = "managed-server"; static String managedServerPodNamePrefix = domainUid + "-" + managedServerNameBase; - static final int managedServerPort = 8001; static int replicaCount = 2; String clusterRes2Name = cluster2Name; String clusterRes1Name = cluster1Name; @@ -347,7 +362,8 @@ void testK8SEventsFailedLifeCycle() { + " Changed and Failed events are logged"); patchStr = "[{\"op\": \"replace\", " + "\"path\": \"/spec/domainHome\", \"value\": \"" + originalDomainHome + "bad\"}," - + "{\"op\": \"replace\", \"path\": \"/spec/serverStartPolicy\", \"value\": \"IfNeeded\"}]"; + + "{\"op\": \"replace\", \"path\": \"/spec/serverStartPolicy\", \"value\": \"IfNeeded\"}," + + "{\"op\": \"remove\", \"path\": \"/spec/configuration/initializeDomainOnPV\"}]"; logger.info("PatchStr for domainHome: {0}", patchStr); patch = new V1Patch(patchStr); @@ -419,8 +435,9 @@ void testDomainK8sEventsScalePastMaxWithoutChangingIntrospectVersion() { void testDomainK8sEventsScalePastMaxAndChangeIntrospectVersion() { OffsetDateTime timestamp = now(); try { - removeReplicasSettingAndVerify(domainUid, cluster1Name, domainNamespace3, replicaCount, + removeReplicasSettingAndVerify(domainUid, cluster1Name, domainNamespace3, replicaCount - 1, managedServerPodNamePrefix); + checkPodDoesNotExist(managedServerPodNamePrefix + 2, domainUid, domainNamespace3); String introspectVersion = assertDoesNotThrow(() -> getNextIntrospectVersion(domainUid, domainNamespace3)); String patchStr @@ -433,7 +450,6 @@ void testDomainK8sEventsScalePastMaxAndChangeIntrospectVersion() { assertTrue(patchDomainCustomResource(domainUid, domainNamespace3, new V1Patch(patchStr), V1Patch.PATCH_FORMAT_JSON_PATCH), "Patch domain did not fail as expected"); - logger.info("verify the Failed event is generated"); checkFailedEvent(opNamespace, domainNamespace3, domainUid, REPLICAS_TOO_HIGH_ERROR, "Warning", timestamp); } finally { @@ -488,13 +504,13 @@ void testK8sEventsFailed() { + "{\"op\": \"replace\", \"path\": \"/spec/serverPod/volumes/0/name\", \"value\": \"sample-pv\"}," + "{\"op\": \"replace\", \"path\": " + "\"/spec/serverPod/volumes/0/persistentVolumeClaim/claimName\", \"value\": \"sample-pvc\"}," - + "{\"op\": \"add\", \"path\": \"/spec/introspectVersion\", \"value\": \"" + introspectVersion + "\"}" - + "]"; + + "{\"op\": \"add\", \"path\": \"/spec/introspectVersion\", \"value\": \"" + introspectVersion + "\"}," + + "{\"op\": \"remove\", \"path\": \"/spec/configuration/initializeDomainOnPV\"}]"; logger.info("Updating pv/pvcs in domain resource using patch string: {0}", patchStr); V1Patch patch = new V1Patch(patchStr); assertTrue(patchDomainCustomResource(domainUid, domainNamespace3, patch, V1Patch.PATCH_FORMAT_JSON_PATCH), "Failed to patch domain"); - + logger.info("verify the Failed event is generated"); checkEvent(opNamespace, domainNamespace3, domainUid, DOMAIN_FAILED, "Warning", timestamp); } finally { @@ -742,10 +758,9 @@ private static DomainResource createDomain(String domainNamespace, String domain // Create and start a WebLogic domain in PV private static DomainResource createDomain(String domainNamespace, String domainUid, - String pvName, String pvcName, String serverStartupPolicy, - UnaryOperator domainSpecUnaryOperator) { - - String uniquePath = "/shared/" + domainNamespace + "/domains"; + String pvName, String pvcName, String serverStartupPolicy, + UnaryOperator domainSpecUnaryOperator) { + String uniqueDomainHome = "/shared/" + domainNamespace + "/domains/"; // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster // this secret is used only for non-kind cluster @@ -753,126 +768,107 @@ private static DomainResource createDomain(String domainNamespace, String domain // create WebLogic domain credential secret createSecretWithUsernamePassword(wlSecretName, domainNamespace, - ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); - - // create persistent volume and persistent volume claim for domain - // these resources should be labeled with domainUid for cleanup after testing - createPV(pvName, domainUid, className); - createPVC(pvName, pvcName, domainUid, domainNamespace); + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + final String wlsModelFilePrefix = "model-dci-introspect"; + final String wlsModelFile = wlsModelFilePrefix + ".yaml"; int t3ChannelPort = getNextFreePort(); - // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), - "Failed to create domain properties file"); - Properties p = new Properties(); - p.setProperty("domain_path", uniquePath); - p.setProperty("domain_name", domainUid); - p.setProperty("cluster_name", cluster1Name); - p.setProperty("admin_server_name", adminServerName); - p.setProperty("managed_server_port", Integer.toString(managedServerPort)); - p.setProperty("admin_server_port", "7001"); - p.setProperty("admin_username", ADMIN_USERNAME_DEFAULT); - p.setProperty("admin_password", ADMIN_PASSWORD_DEFAULT); - p.setProperty("admin_t3_public_address", K8S_NODEPORT_HOST); - p.setProperty("admin_t3_channel_port", Integer.toString(t3ChannelPort)); - p.setProperty("number_of_ms", "2"); - p.setProperty("managed_server_name_base", managedServerNameBase); - p.setProperty("domain_logs", "/shared/" + domainNamespace + "/logs/" + domainUid); - p.setProperty("production_mode_enabled", "true"); - assertDoesNotThrow(() - -> p.store(new FileOutputStream(domainPropertiesFile), "domain properties file"), - "Failed to write domain properties file"); - - // WLST script for creating domain - Path wlstScript = Paths.get(RESOURCE_DIR, "python-scripts", "wlst-create-domain-onpv.py"); - - // create configmap and domain on persistent volume using the WLST script and property file - createDomainOnPVUsingWlst(wlstScript, domainPropertiesFile.toPath(), - pvName, pvcName, domainNamespace); + File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + K8S_NODEPORT_HOST, t3ChannelPort); + + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "events-domain-on-pv-image"; + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) + .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + + // create a domain resource + logger.info("Creating domain custom resource"); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); + + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + final String storageClassName = "weblogic-domain-storage-class"; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + ItIntrospectVersion.class.getName()); + } + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); + configuration.overrideDistributionStrategy("Dynamic"); + + // create secrets + List secrets = new ArrayList<>(); + for (String secret : new String[]{wlSecretName, BASE_IMAGES_REPO_SECRET_NAME}) { + secrets.add(new V1LocalObjectReference().name(secret)); + } // create a domain custom resource configuration object - logger.info("Creating domain custom resource"); DomainResource domain = new DomainResource() - .apiVersion(DOMAIN_API_VERSION) - .kind("Domain") - .metadata(new V1ObjectMeta() - .name(domainUid) - .namespace(domainNamespace)) - .spec(domainSpecUnaryOperator.apply(new DomainSpec() - .domainUid(domainUid) - .domainHome(uniquePath + "/" + domainUid) // point to domain home in pv - .domainHomeSourceType("PersistentVolume") // set the domain home source type as pv - .replicas(replicaCount) - .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) - .imagePullPolicy(IMAGE_PULL_POLICY) - .imagePullSecrets(Arrays.asList( - new V1LocalObjectReference() - .name(BASE_IMAGES_REPO_SECRET_NAME))) // secret for non-kind cluster - .webLogicCredentialsSecret(new V1LocalObjectReference() - .name(wlSecretName)) - .includeServerOutInPodLog(true) - .logHomeEnabled(Boolean.TRUE) - .logHome("/shared/" + domainNamespace + "/logs/" + domainUid + "/") - .dataHome("") - .serverStartPolicy(serverStartupPolicy) - .serverPod(new ServerPod() //serverpod - .addEnvItem(new V1EnvVar() - .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom ")) - .addVolumesItem(new V1Volume() - .name(pvName) - .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() - .claimName(pvcName))) - .addVolumeMountsItem(new V1VolumeMount() - .mountPath("/shared") - .name(pvName))))); - setPodAntiAffinity(domain); - domain = createClusterResourceAndAddReferenceToDomain( - cluster1Name, cluster1Name, domainNamespace, domain, replicaCount); - assertNotNull(domain, "Failed to add Cluster to domain"); + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new V1ObjectMeta() + .name(domainUid) + .namespace(domainNamespace)) + .spec(domainSpecUnaryOperator.apply(new DomainSpec() + .domainUid(domainUid) + .domainHome(uniqueDomainHome + domainUid) + .domainHomeSourceType("PersistentVolume") + .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) + .imagePullPolicy(IMAGE_PULL_POLICY) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(wlSecretName)) + .includeServerOutInPodLog(true) + .logHomeEnabled(Boolean.TRUE) + .logHome("/shared/" + domainNamespace + "/logs/" + domainUid) + .dataHome("") + .serverStartPolicy(serverStartupPolicy) + .failureRetryIntervalSeconds(FAILURE_RETRY_INTERVAL_SECONDS) + .failureRetryLimitMinutes(FAILURE_RETRY_LIMIT_MINUTES) + .serverPod(new ServerPod() //serverpod + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false")) + .addEnvItem(new V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom")) + .addVolumesItem(new V1Volume() + .name(pvName) + .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() + .claimName(pvcName))) + .addVolumeMountsItem(new V1VolumeMount() + .mountPath("/shared") + .name(pvName))) + .adminServer(new AdminServer() //admin server + .adminService(new AdminService())) + .configuration(configuration))); + domain.spec().setImagePullSecrets(secrets); + + // create cluster resource for the domain + if (!Cluster.doesClusterExist(cluster1Name, CLUSTER_VERSION, domainNamespace)) { + ClusterResource cluster = createClusterResource(cluster1Name, + cluster1Name, domainNamespace, replicaCount); + createClusterAndVerify(cluster); + } + domain.getSpec().withCluster(new V1LocalObjectReference().name(cluster1Name)); + createDomainAndVerify(domain, domainNamespace); return domain; } - /** - * Create a WebLogic domain on a persistent volume by doing the following. Create a configmap containing WLST script - * and property file. Create a Kubernetes job to create domain on persistent volume. - * - * @param wlstScriptFile python script to create domain - * @param domainPropertiesFile properties file containing domain configuration - * @param pvName name of the persistent volume to create domain in - * @param pvcName name of the persistent volume claim - * @param namespace name of the domain namespace in which the job is created - */ - private static void createDomainOnPVUsingWlst(Path wlstScriptFile, Path domainPropertiesFile, - String pvName, String pvcName, String namespace) { - logger.info("Preparing to run create domain job using WLST"); - - List domainScriptFiles = new ArrayList<>(); - domainScriptFiles.add(wlstScriptFile); - domainScriptFiles.add(domainPropertiesFile); - - logger.info("Creating a config map to hold domain creation scripts"); - String domainScriptConfigMapName = "create-domain-scripts-cm"; - assertDoesNotThrow( - () -> createConfigMapForDomainCreation( - domainScriptConfigMapName, domainScriptFiles, namespace, className), - "Create configmap for domain creation failed"); - - // create a V1Container with specific scripts and properties for creating domain - V1Container jobCreationContainer = new V1Container() - .addCommandItem("/bin/sh") - .addArgsItem("/u01/oracle/oracle_common/common/bin/wlst.sh") - .addArgsItem("/u01/weblogic/" + wlstScriptFile.getFileName()) //wlst.sh script - .addArgsItem("-skipWLSModuleScanning") - .addArgsItem("-loadProperties") - .addArgsItem("/u01/weblogic/" + domainPropertiesFile.getFileName()); //domain property file - - logger.info("Running a Kubernetes job to create the domain"); - createDomainJob(WEBLOGIC_IMAGE_TO_USE_IN_SPEC, pvName, pvcName, domainScriptConfigMapName, - namespace, jobCreationContainer); - } - private void createNewCluster() { final String managedServerNameBase = "cl2-ms-"; String managedServerPodNamePrefix = domainUid + "-" + managedServerNameBase; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index 17848275226..4f2e9cb4952 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -4,21 +4,20 @@ package oracle.weblogic.kubernetes; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.net.http.HttpResponse; import java.nio.file.Path; import java.nio.file.Paths; import java.time.OffsetDateTime; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.concurrent.Callable; +import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvVar; @@ -30,12 +29,19 @@ import oracle.weblogic.domain.AdminServer; import oracle.weblogic.domain.AdminService; import oracle.weblogic.domain.Channel; +import oracle.weblogic.domain.ClusterResource; import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.assertions.impl.Cluster; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.ExecResult; import oracle.weblogic.kubernetes.utils.OracleHttpClient; @@ -47,14 +53,17 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; -import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.getNextIntrospectVersion; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; @@ -64,8 +73,10 @@ import static oracle.weblogic.kubernetes.actions.impl.Domain.patchDomainCustomResource; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podStateNotChanged; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.BuildApplication.buildApplication; -import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; @@ -79,13 +90,12 @@ import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapFromFiles; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVC; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDoesNotExist; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; @@ -371,49 +381,63 @@ private void verifyIntrospectorRuns() { //create a standard WebLogic domain. private void createDomain() { - String uniquePath = "/shared/" + domainNamespace + "/domains"; + String uniqueDomainHome = "/shared/" + domainNamespace + "/domains/"; + + // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster + // this secret is used only for non-kind cluster + createBaseRepoSecret(domainNamespace); // create WebLogic domain credential secret createSecretWithUsernamePassword(wlSecretName, domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); - - createPV(pvName, domainUid, this.getClass().getSimpleName()); - createPVC(pvName, pvcName, domainUid, domainNamespace); - + final String wlsModelFilePrefix = "sitconfig-dci-model"; + final String wlsModelFile = wlsModelFilePrefix + ".yaml"; t3ChannelPort = getNextFreePort(); + File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + K8S_NODEPORT_HOST, t3ChannelPort); + + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "sitconfig-domain-on-pv-image"; + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) + .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + + // create a domain resource + logger.info("Creating domain custom resource"); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); - // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() - -> File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), - "Failed to create domain properties file"); - Properties p = new Properties(); - p.setProperty("domain_path", uniquePath); - p.setProperty("domain_name", domainUid); - p.setProperty("cluster_name", clusterName); - p.setProperty("admin_server_name", adminServerName); - p.setProperty("managed_server_port", Integer.toString(managedServerPort)); - p.setProperty("admin_server_port", "7001"); - p.setProperty("admin_username", ADMIN_USERNAME_DEFAULT); - p.setProperty("admin_password", ADMIN_PASSWORD_DEFAULT); - p.setProperty("admin_t3_public_address", K8S_NODEPORT_HOST); - p.setProperty("admin_t3_channel_port", Integer.toString(t3ChannelPort)); - p.setProperty("number_of_ms", "2"); - p.setProperty("managed_server_name_base", managedServerNameBase); - p.setProperty("domain_logs", uniquePath + "/logs/" + domainUid); - p.setProperty("production_mode_enabled", "true"); - assertDoesNotThrow(() - -> p.store(new FileOutputStream(domainPropertiesFile), "domain properties file"), - "Failed to write domain properties file"); - - // WLST script for creating domain - Path wlstScript = Paths.get(RESOURCE_DIR, "python-scripts", "sit-config-create-domain.py"); - - // create configmap and domain on persistent volume using the WLST script and property file - createDomainOnPVUsingWlst(wlstScript, domainPropertiesFile.toPath(), - pvName, pvcName, domainNamespace); + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + final String storageClassName = "weblogic-domain-storage-class"; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + ItIntrospectVersion.class.getName()); + } + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); + configuration.overrideDistributionStrategy("Dynamic"); + + // create secrets + List secrets = new ArrayList<>(); + for (String secret : new String[]{wlSecretName, BASE_IMAGES_REPO_SECRET_NAME}) { + secrets.add(new V1LocalObjectReference().name(secret)); + } // create a domain custom resource configuration object - logger.info("Creating domain custom resource"); DomainResource domain = new DomainResource() .apiVersion(DOMAIN_API_VERSION) .kind("Domain") @@ -421,21 +445,16 @@ private void createDomain() { .name(domainUid) .namespace(domainNamespace)) .spec(new DomainSpec() - .configuration(new Configuration() - .overrideDistributionStrategy("Dynamic")) .domainUid(domainUid) - .domainHome(uniquePath + "/" + domainUid) // point to domain home in pv - .domainHomeSourceType("PersistentVolume") // set the domain home source type as pv + .domainHome(uniqueDomainHome + domainUid) + .domainHomeSourceType("PersistentVolume") .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) .imagePullPolicy(IMAGE_PULL_POLICY) - .imagePullSecrets(Arrays.asList( - new V1LocalObjectReference() - .name(BASE_IMAGES_REPO_SECRET_NAME))) // this secret is used only for non-kind cluster .webLogicCredentialsSecret(new V1LocalObjectReference() .name(wlSecretName)) .includeServerOutInPodLog(true) .logHomeEnabled(Boolean.TRUE) - .logHome(uniquePath + "/logs/" + domainUid) + .logHome("/shared/" + domainNamespace + "/logs/" + domainUid) .dataHome("") .serverStartPolicy("IfNeeded") .serverPod(new ServerPod() //serverpod @@ -447,12 +466,12 @@ private void createDomain() { + "-Dweblogic.debug.DebugMessaging=true " + "-Dweblogic.debug.DebugConnection=true " + "-Dweblogic.ResolveDNSName=true")) - .addEnvItem(new V1EnvVar() - .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom ")) .addEnvItem(new V1EnvVar() .name("CUSTOM_ENV") .value("##~`!^${ls}")) + .addEnvItem(new V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom")) .addVolumesItem(new V1Volume() .name(pvName) .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() @@ -464,14 +483,22 @@ private void createDomain() { .adminService(new AdminService() .addChannelsItem(new Channel() .channelName("default") - .nodePort(getNextFreePort())) + .nodePort(0)) .addChannelsItem(new Channel() .channelName("T3Channel") - .nodePort(t3ChannelPort))))); + .nodePort(t3ChannelPort)))) + .configuration(configuration)); + domain.spec().setImagePullSecrets(secrets); + + // create cluster resource for the domain + if (!Cluster.doesClusterExist(clusterName, CLUSTER_VERSION, domainNamespace)) { + ClusterResource cluster = createClusterResource(clusterName, + clusterName, domainNamespace, replicaCount); + createClusterAndVerify(cluster); + } + domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterName)); + setPodAntiAffinity(domain); - // create cluster object - domain = createClusterResourceAndAddReferenceToDomain( - domainUid + "-" + clusterName, clusterName, domainNamespace, domain, replicaCount); // verify the domain custom resource is created createDomainAndVerify(domain, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 1af3451f4b8..89c33972845 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -4,19 +4,17 @@ package oracle.weblogic.kubernetes; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.net.http.HttpResponse; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Properties; -import io.kubernetes.client.openapi.models.V1Container; +import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1ObjectMeta; @@ -26,11 +24,19 @@ import oracle.weblogic.domain.AdminServer; import oracle.weblogic.domain.AdminService; import oracle.weblogic.domain.Channel; +import oracle.weblogic.domain.ClusterResource; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.assertions.impl.Cluster; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.BuildApplication; import oracle.weblogic.kubernetes.utils.ExecCommand; @@ -45,34 +51,38 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DEFAULT_LISTEN_PORT; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS; +import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; -import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; -import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.getServicePort; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; -import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapForDomainCreation; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; -import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; -import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVC; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -96,7 +106,7 @@ class ItT3Channel { // domain constants private final String domainUid = "t3channel-domain"; - private final String clusterName = "cluster-1"; + private final String clusterName = "mycluster"; private final int replicaCount = 2; private final String adminServerName = ADMIN_SERVER_NAME_BASE; private final String adminServerPodName = domainUid + "-" + adminServerName; @@ -138,11 +148,8 @@ public static void initAll(@Namespaces(2) List namespaces) { @Test @DisplayName("Test admin server t3 channel access by deploying a application") void testAdminServerT3Channel() { - - // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster - // this secret is used only for non-kind cluster - createBaseRepoSecret(domainNamespace); - + String uniqueDomainHome = "/shared/" + domainNamespace + "/domains/"; + // build the clusterview application Path distDir = BuildApplication.buildApplication(Paths.get(APP_DIR, "clusterview"), null, null, "dist", domainNamespace); @@ -156,70 +163,61 @@ void testAdminServerT3Channel() { final String pvName = getUniqueName(domainUid + "-pv-"); final String pvcName = getUniqueName(domainUid + "-pvc-"); + // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster + // this secret is used only for non-kind cluster + createBaseRepoSecret(domainNamespace); + // create WebLogic domain credential secret String wlSecretName = "t3weblogic-credentials"; createSecretWithUsernamePassword(wlSecretName, domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + + final String wlsModelFilePrefix = "model-dci-introspect"; + final String wlsModelFile = wlsModelFilePrefix + ".yaml"; + File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + K8S_NODEPORT_HOST, t3ChannelPort); + + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "t3channel-domain-on-pv-image"; + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) + .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + + // create a domain resource + logger.info("Creating domain custom resource"); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); - // create persistent volume and persistent volume claim for domain - // these resources should be labeled with domainUid for cleanup after testing - createPV(pvName, domainUid, this.getClass().getSimpleName()); - createPVC(pvName, pvcName, domainUid, domainNamespace); - - // create a temporary WebLogic domain property file - File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile("domain", ".properties", new File(RESULTS_TEMPFILE)), - //File.createTempFile("domain", "properties"), - "Failed to create domain properties file"); - Properties p = new Properties(); - p.setProperty("domain_path", "/shared/" + domainNamespace + "/domains"); - p.setProperty("domain_name", domainUid); - p.setProperty("cluster_name", clusterName); - p.setProperty("admin_server_name", adminServerName); - p.setProperty("managed_server_port", "8001"); - p.setProperty("admin_server_port", "7001"); - p.setProperty("admin_username", ADMIN_USERNAME_DEFAULT); - p.setProperty("admin_password", ADMIN_PASSWORD_DEFAULT); - p.setProperty("admin_t3_public_address", K8S_NODEPORT_HOST); - p.setProperty("admin_t3_channel_port", Integer.toString(t3ChannelPort)); - p.setProperty("number_of_ms", "2"); - p.setProperty("managed_server_name_base", MANAGED_SERVER_NAME_BASE); - p.setProperty("domain_logs", "/shared/" + domainNamespace + "/logs/" + domainUid); - p.setProperty("production_mode_enabled", "true"); - assertDoesNotThrow(() -> - p.store(new FileOutputStream(domainPropertiesFile), "domain properties file"), - "Failed to write domain properties file"); - - // WLST script for creating domain - Path wlstScript = Paths.get(RESOURCE_DIR, "python-scripts", "wlst-create-domain-onpv.py"); - - logger.info("Preparing to run create domain job using WLST"); - - List domainScriptFiles = new ArrayList<>(); - domainScriptFiles.add(wlstScript); - domainScriptFiles.add(domainPropertiesFile.toPath()); - - logger.info("Creating a config map to hold domain creation scripts"); - String domainScriptConfigMapName = "create-domain-scripts-cm"; - assertDoesNotThrow(() -> createConfigMapForDomainCreation(domainScriptConfigMapName, domainScriptFiles, - domainNamespace, this.getClass().getSimpleName()), - "Create configmap for domain creation failed"); - - // create a V1Container with specific scripts and properties for creating domain - V1Container jobCreationContainer = new V1Container() - .addCommandItem("/bin/sh") - .addArgsItem("/u01/oracle/oracle_common/common/bin/wlst.sh") - .addArgsItem("/u01/weblogic/" + wlstScript.getFileName()) //wlst.sh script - .addArgsItem("-skipWLSModuleScanning") - .addArgsItem("-loadProperties") - .addArgsItem("/u01/weblogic/" + domainPropertiesFile.getName()); //domain property file - - logger.info("Running a Kubernetes job to create the domain"); - createDomainJob(WEBLOGIC_IMAGE_TO_USE_IN_SPEC, pvName, pvcName, domainScriptConfigMapName, - domainNamespace, jobCreationContainer); + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + final String storageClassName = "weblogic-domain-storage-class"; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + ItIntrospectVersion.class.getName()); + } + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); + + // create secrets + List secrets = new ArrayList<>(); + for (String secret : new String[]{wlSecretName, BASE_IMAGES_REPO_SECRET_NAME}) { + secrets.add(new V1LocalObjectReference().name(secret)); + } // create a domain custom resource configuration object - logger.info("Creating domain custom resource"); DomainResource domain = new DomainResource() .apiVersion(DOMAIN_API_VERSION) .kind("Domain") @@ -228,14 +226,10 @@ void testAdminServerT3Channel() { .namespace(domainNamespace)) .spec(new DomainSpec() .domainUid(domainUid) - .domainHome("/shared/" + domainNamespace + "/domains/" + domainUid) // point to domain home in pv - .domainHomeSourceType("PersistentVolume") // set the domain home source type as pv + .domainHome(uniqueDomainHome + domainUid) + .domainHomeSourceType("PersistentVolume") .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) - .replicas(replicaCount) .imagePullPolicy(IMAGE_PULL_POLICY) - .imagePullSecrets(Arrays.asList( - new V1LocalObjectReference() - .name(BASE_IMAGES_REPO_SECRET_NAME))) // this secret is used only in non-kind cluster .webLogicCredentialsSecret(new V1LocalObjectReference() .name(wlSecretName)) .includeServerOutInPodLog(true) @@ -243,10 +237,15 @@ void testAdminServerT3Channel() { .logHome("/shared/" + domainNamespace + "/logs/" + domainUid) .dataHome("") .serverStartPolicy("IfNeeded") + .failureRetryIntervalSeconds(FAILURE_RETRY_INTERVAL_SECONDS) + .failureRetryLimitMinutes(FAILURE_RETRY_LIMIT_MINUTES) .serverPod(new ServerPod() //serverpod + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false")) .addEnvItem(new V1EnvVar() .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom ")) + .value("-Djava.security.egd=file:/dev/./urandom")) .addVolumesItem(new V1Volume() .name(pvName) .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() @@ -258,11 +257,21 @@ void testAdminServerT3Channel() { .adminService(new AdminService() .addChannelsItem(new Channel() .channelName("default") - .nodePort(getNextFreePort())) + .nodePort(0)) .addChannelsItem(new Channel() .channelName("T3Channel") - .nodePort(t3ChannelPort))))); - + .nodePort(t3ChannelPort)))) + .configuration(configuration)); + domain.spec().setImagePullSecrets(secrets); + + // create cluster resource for the domain + if (!Cluster.doesClusterExist(clusterName, CLUSTER_VERSION, domainNamespace)) { + ClusterResource cluster = createClusterResource(clusterName, + clusterName, domainNamespace, replicaCount); + createClusterAndVerify(cluster); + } + domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterName)); + // verify the domain custom resource is created createDomainAndVerify(domain, domainNamespace); @@ -322,7 +331,7 @@ void testAdminServerT3Channel() { -> getServicePort(domainNamespace, domainUid + "-cluster-" + clusterName, "ms-nap"), "Getting Cluster Service nap port failed"); - assertEquals(8011, napPort, "Nap Service Port is not set to 8011"); + assertEquals(7110, napPort, "Nap Service Port is not set to 7110"); servicePort = assertDoesNotThrow(() -> getServicePort(domainNamespace, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Cluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Cluster.java index c1228cf6ab8..b2e07534b5b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Cluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Cluster.java @@ -89,7 +89,7 @@ public static boolean scaleCluster(String clusterRes, String namespace, int numO logger.info("ClusterResource {0} not found in NameSpace {1}", clusterRes, namespace); return false; } - + // construct the patch string for scaling the cluster StringBuffer patchStr = new StringBuffer("[{") .append("\"op\": \"replace\", ") diff --git a/integration-tests/src/test/resources/python-scripts/introspect_version_script.py b/integration-tests/src/test/resources/python-scripts/introspect_version_script.py index bfe45087225..56d18df90f1 100644 --- a/integration-tests/src/test/resources/python-scripts/introspect_version_script.py +++ b/integration-tests/src/test/resources/python-scripts/introspect_version_script.py @@ -32,7 +32,7 @@ def change_server_count(): connect_to_adminserver() edit() startEdit() - cd('/Clusters/' + cluster_name + '/DynamicServers/' + cluster_name) + cd('/Clusters/' + cluster_name + '/DynamicServers/NO_NAME_0') cmo.setDynamicClusterSize(int(max_cluster_size)) cmo.setMaxDynamicClusterSize(int(max_cluster_size)) save() diff --git a/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml b/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml new file mode 100644 index 00000000000..1533b993fcf --- /dev/null +++ b/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml @@ -0,0 +1,33 @@ +domainInfo: + AdminUserName: '@@PROP:WebLogicAdminUserName@@' + AdminPassword: '@@PROP:WebLogicAdminPassword@@' +topology: + Name: mydomain + AdminServerName: admin-server + ProductionModeEnabled: true + Cluster: + mycluster: + WeblogicPluginEnabled: true + DynamicServers: + DynamicClusterSize: 2 + MaxDynamicClusterSize: 2 + ServerNamePrefix: managed-server + ServerTemplate: mycluster-template + CalculatedListenPorts: false + MinDynamicClusterSize: 2 + Server: + admin-server: + WeblogicPluginEnabled: true + NetworkAccessPoint: + T3Channel: + PublicAddress: '@@PROP:K8S_NODEPORT_HOST@@' + ListenPort: '@@PROP:T3_CHANNEL_PORT@@' + PublicPort: '@@PROP:T3_CHANNEL_PORT@@' + ServerTemplate: + mycluster-template: + Cluster: mycluster + ResolveDNSName: true + NetworkAccessPoint: + ms-nap: + ListenPort: 7110 + diff --git a/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml b/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml new file mode 100644 index 00000000000..0120d24fabe --- /dev/null +++ b/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml @@ -0,0 +1,63 @@ +domainInfo: + AdminUserName: '@@PROP:WebLogicAdminUserName@@' + AdminPassword: '@@PROP:WebLogicAdminPassword@@' +topology: + Name: mysitconfigdomain + AdminServerName: admin-server + ProductionModeEnabled: true + Cluster: + mycluster: + DynamicServers: + DynamicClusterSize: 2 + MaxDynamicClusterSize: 2 + ServerNamePrefix: ms- + ServerTemplate: mycluster-template + CalculatedListenPorts: false + Server: + admin-server: + NetworkAccessPoint: + T3Channel: + PublicAddress: '@@PROP:K8S_NODEPORT_HOST@@' + ListenPort: '@@PROP:T3_CHANNEL_PORT@@' + PublicPort: '@@PROP:T3_CHANNEL_PORT@@' + ServerTemplate: + mycluster-template: + Cluster: mycluster + ResolveDNSName: true + ListenPort: 8001 + NetworkAccessPoint: + ms-nap: + ListenPort: 8011 +resources: + FileStore: + ClusterFileStore: + Target: mycluster + Directory: JmsFileStores + JMSServer: + ClusterJmsServer: + Target: mycluster + PersistentStore: ClusterFileStore + JMSSystemResource: + ClusterJmsSystemResource: + Target: mycluster + SubDeployment: + ClusterSubDeployment: + Target: mycluster + JmsResource: + ConnectionFactory: + ClusterConnectionFactory: + DefaultTargetingEnabled: true + JNDIName: jms/ClusterConnectionFactory + UniformDistributedQueue: + UniformDistributedTestQueue: + JNDIName: jms/UniformDistributedTestQueue + DefaultTargetingEnabled: true + UniformDistributedTopic: + UniformReplicatedTestTopic: + JNDIName: jms/UniformReplicatedTestTopic + DefaultTargetingEnabled: true + DeliveryFailureParams: + ExpirationPolicy: Log + WLDFSystemResource: + WLDF-MODULE-0: + Target: mycluster \ No newline at end of file From 1df2aa8ceee7f436f7b01b5a1bf6d128cd6875d3 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 3 Jul 2024 21:45:24 +0000 Subject: [PATCH 098/356] Backport changes for Converted DBOperator tests from external to internal run in OKE --- Jenkinsfile.oke | 2 +- .../weblogic/kubernetes/ItDBOperator.java | 141 +++++++++--------- .../kubernetes/ItFmwDomainInPVUsingWDT.java | 2 +- .../kubernetes/ItFmwDomainInPVUsingWLST.java | 2 +- .../ItFmwDomainInPvUserCreateRcu.java | 2 +- .../weblogic/kubernetes/ItFmwDomainOnPV.java | 14 +- .../kubernetes/ItFmwDynamicDomainInPV.java | 41 ++++- .../kubernetes/utils/CommonMiiTestUtils.java | 33 ++-- .../kubernetes/utils/CommonTestUtils.java | 9 +- 9 files changed, 147 insertions(+), 99 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 1d195c5d0b6..b00390a1a21 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -141,7 +141,7 @@ pipeline { ) string(name: 'NODE_SHAPE', description: '', - defaultValue: "VM.Standard.E3.Flex" + defaultValue: "VM.Standard.E4.Flex" ) string(name: 'MOUNT_TARGET_OCID', description: 'only for debug runs on wko-oke-dev', diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java index 144aa1b7d03..26d04cea7e3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java @@ -15,8 +15,10 @@ import io.kubernetes.client.openapi.models.V1LocalObjectReference; import oracle.weblogic.domain.ClusterResource; import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.kubernetes.actions.impl.TraefikParams; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; +import oracle.weblogic.kubernetes.actions.impl.primitive.HelmParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -41,18 +43,18 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; -import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.actions.ActionConstants.ITTESTS_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.createDomainCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.execCommand; -import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.patchDomainCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.scaleCluster; +import static oracle.weblogic.kubernetes.actions.TestActions.uninstallTraefik; import static oracle.weblogic.kubernetes.actions.impl.primitive.Command.defaultCommandParams; import static oracle.weblogic.kubernetes.assertions.TestAssertions.domainExists; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; @@ -61,9 +63,9 @@ import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createDomainResourceWithLogHome; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createDomainSecret; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createJobToChangePermissionsOnPvHostPath; +import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.readRuntimeResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runClientInsidePod; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runJavacInsidePod; @@ -74,7 +76,6 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.deleteOracleDB; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; -import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyEMconsoleAccess; @@ -82,6 +83,7 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createMiiImageAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyTraefik; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResourceServerStartPolicy; @@ -93,6 +95,7 @@ import static oracle.weblogic.kubernetes.utils.SecretUtils.createOpsswalletpasswordSecret; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -106,7 +109,7 @@ @DisplayName("Test to create FMW model in image domain and WebLogic domain using Oracle " + "database created using Oracle Database Operator") @IntegrationTest -@Tag("oke-sequential") +@Tag("oke-gate") @Tag("kind-parallel") class ItDBOperator { @@ -114,6 +117,7 @@ class ItDBOperator { private static String opNamespace = null; private static String fmwDomainNamespace = null; private static String wlsDomainNamespace = null; + private static String traefikNamespace = null; private static String fmwMiiImage = null; private static final String RCUSCHEMAPREFIX = "FMWDOMAINMII"; @@ -124,6 +128,7 @@ class ItDBOperator { private static String dbUrl = null; private static String dbName = "my-oracle-sidb"; private static LoggingFacade logger = null; + private static HelmParams traefikHelmParams; private String fmwDomainUid = "fmwdomain-mii-db"; private String adminServerName = "admin-server"; @@ -153,6 +158,7 @@ class ItDBOperator { private final String wlsClusterResName = wlsDomainUid + "-" + clusterName; private static String hostHeader; + private static TraefikParams traefikParams; /** * Start DB service and create RCU schema. @@ -162,7 +168,7 @@ class ItDBOperator { * @param namespaces injected by JUnit */ @BeforeAll - public static void initAll(@Namespaces(4) List namespaces) { + public static void initAll(@Namespaces(5) List namespaces) { logger = getLogger(); logger.info("Assign a unique namespace for DB and RCU"); @@ -181,6 +187,18 @@ public static void initAll(@Namespaces(4) List namespaces) { assertNotNull(namespaces.get(3), "Namespace is null"); wlsDomainNamespace = namespaces.get(3); + // get a unique Traefik namespace + logger.info("Get a unique namespace for Traefik"); + assertNotNull(namespaces.get(4), "Namespace list is null"); + traefikNamespace = namespaces.get(4); + + // install and verify Traefik + if (OKE_CLUSTER_PRIVATEIP) { + traefikParams = + installAndVerifyTraefik(traefikNamespace, 0, 0); + traefikHelmParams = traefikParams.getHelmParams(); + } + // Create the repo secret to pull the image // this secret is used only for non-kind cluster createBaseRepoSecret(fmwDomainNamespace); @@ -199,6 +217,17 @@ public static void initAll(@Namespaces(4) List namespaces) { installAndVerifyOperator(opNamespace, fmwDomainNamespace, wlsDomainNamespace); } + @AfterAll + void tearDown() { + // uninstall Traefik + if (traefikHelmParams != null) { + assertThat(uninstallTraefik(traefikHelmParams)) + .as("Test uninstallTraefik returns true") + .withFailMessage("uninstallTraefik() did not return true") + .isTrue(); + } + } + /** * Create a basic FMW model in image domain using the database created by DB Operator. Verify Pod is ready and service * exists for both admin server and managed servers. Verify EM console is accessible. @@ -553,34 +582,28 @@ private void runJmsClientOnAdminPod(String action, String queue) { * @returns true if MBean is found otherwise false **/ private boolean checkJmsServerRuntime(String jmsServer, String managedServer) { - ExecResult result = null; - int adminServiceNodePort - = getServiceNodePort(wlsDomainNamespace, getExternalServicePodName(wlsAdminServerPodName), "default"); - String hostAndPort = getHostAndPort(adminSvcExtRouteHost, adminServiceNodePort); - StringBuffer curlString = new StringBuffer("status=$(curl --user " - + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT + " "); - if (TestConstants.KIND_CLUSTER - && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; - curlString.append(" -H 'host: " + hostHeader + "' "); - } - curlString.append("http://" + hostAndPort) - .append("/management/weblogic/latest/domainRuntime/serverRuntimes/") - .append(managedServer) - .append("/JMSRuntime/JMSServers/") - .append(jmsServer) - .append(" --silent --show-error ") - .append(" -o /dev/null") - .append(" -w %{http_code});") - .append("echo ${status}"); - logger.info("checkJmsServerRuntime: curl command {0}", new String(curlString)); testUntil( - assertDoesNotThrow(() -> () -> exec(curlString.toString(), true).stdout().contains("200")), + assertDoesNotThrow(() -> () -> getJMSRunTimeOutput(jmsServer, + managedServer).contains("destinationsCurrentCount")), logger, "JMS Server Service to migrate"); return true; } + private String getJMSRunTimeOutput(String jmsServer, String managedServer) { + String output = readRuntimeResource( + adminSvcExtHost, + wlsDomainNamespace, + wlsAdminServerPodName, + "/management/weblogic/latest/domainRuntime/serverRuntimes/" + + managedServer + + "/JMSRuntime/JMSServers/" + + jmsServer, + "checkJmsServerRuntime"); + logger.info("Got output " + output); + return output; + } + /* * Verify the Persistent Store Runtimes through REST API. * Get the specific Persistent Store Runtime on specified managed server. @@ -589,31 +612,19 @@ private boolean checkJmsServerRuntime(String jmsServer, String managedServer) { * @returns true if MBean is found otherwise false **/ private boolean checkStoreRuntime(String storeName, String managedServer) { - ExecResult result = null; - int adminServiceNodePort - = getServiceNodePort(wlsDomainNamespace, getExternalServicePodName(wlsAdminServerPodName), "default"); - String hostAndPort = getHostAndPort(adminSvcExtRouteHost, adminServiceNodePort); - StringBuffer curlString = new StringBuffer("status=$(curl --user " - + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT + " "); - if (TestConstants.KIND_CLUSTER - && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; - curlString.append(" -H 'host: " + hostHeader + "' "); - } - curlString.append("http://" + hostAndPort) - .append("/management/weblogic/latest/domainRuntime/serverRuntimes/") - .append(managedServer) - .append("/persistentStoreRuntimes/") - .append(storeName) - .append(" --silent --show-error ") - .append(" -o /dev/null") - .append(" -w %{http_code});") - .append("echo ${status}"); - logger.info("checkStoreRuntime: curl command {0}", new String(curlString)); testUntil( - assertDoesNotThrow(() -> () -> exec(curlString.toString(), true).stdout().contains("200")), + assertDoesNotThrow(() -> () -> readRuntimeResource( + adminSvcExtHost, + wlsDomainNamespace, + wlsAdminServerPodName, + "/management/weblogic/latest/domainRuntime/serverRuntimes/" + + managedServer + + "/persistentStoreRuntimes/" + + storeName, + "checkPersistentStoreRuntime").contains("PersistentStoreRuntime")), logger, "PersistentStoreRuntimes Service to migrate"); + return true; } @@ -627,28 +638,16 @@ private boolean checkStoreRuntime(String storeName, String managedServer) { * @returns true if MBean is found otherwise false **/ private boolean checkJtaRecoveryServiceRuntime(String managedServer, String recoveryService, String active) { - ExecResult result = null; - int adminServiceNodePort - = getServiceNodePort(wlsDomainNamespace, getExternalServicePodName(wlsAdminServerPodName), "default"); - String hostAndPort = getHostAndPort(adminSvcExtRouteHost, adminServiceNodePort); - StringBuffer curlString = new StringBuffer("curl --user " - + ADMIN_USERNAME_DEFAULT + ":" + ADMIN_PASSWORD_DEFAULT + " "); - if (TestConstants.KIND_CLUSTER - && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostAndPort = "localhost:" + TRAEFIK_INGRESS_HTTP_HOSTPORT; - curlString.append(" -H 'host: " + hostHeader + "' "); - } - curlString.append("\"http://" + hostAndPort) - .append("/management/weblogic/latest/domainRuntime/serverRuntimes/") - .append(managedServer) - .append("/JTARuntime/recoveryRuntimeMBeans/") - .append(recoveryService) - .append("?fields=active&links=none\"") - .append(" --show-error "); - logger.info("checkJtaRecoveryServiceRuntime: curl command {0}", new String(curlString)); testUntil( - assertDoesNotThrow(() -> () -> exec(curlString.toString(), true) - .stdout().contains("\"active\": " + active)), + assertDoesNotThrow(() -> () -> readRuntimeResource( + adminSvcExtHost, + wlsDomainNamespace, + wlsAdminServerPodName, + "/management/weblogic/latest/domainRuntime/serverRuntimes/" + + managedServer + + "/JTARuntime/recoveryRuntimeMBeans/" + + recoveryService, + "checkRecoveryServiceRuntime").contains("\"active\": " + active)), logger, "JTA Recovery Service to migrate"); return true; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java index 6fcfa5353c7..b97d6888dd5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java @@ -59,7 +59,7 @@ */ @DisplayName("Test to creat a FMW domain in persistent volume using WDT") @IntegrationTest -@Tag("oke-sequential") +@Tag("oke-gate") @Tag("kind-sequential") @Tag("okd-fmw-cert") class ItFmwDomainInPVUsingWDT { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index d9b83d04e6b..b8616b28061 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -72,7 +72,7 @@ @Tag("okd-fmw-cert") @IntegrationTest @Tag("olcne-sequential") -@Tag("oke-sequential1") +@Tag("oke-gate") class ItFmwDomainInPVUsingWLST { private static String dbNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java index 5c40727fe70..544e7ac413e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java @@ -99,7 +99,7 @@ @DisplayName("Test for initializeDomainOnPV when user per-creates RCU") @IntegrationTest @Tag("kind-sequential") -@Tag("oke-sequential1") +@Tag("oke-gate") public class ItFmwDomainInPvUserCreateRcu { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index cfec7cbbdc8..46dc2322728 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -39,7 +39,6 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; -import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.ELASTICSEARCH_HOST; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; @@ -64,9 +63,9 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; -import static oracle.weblogic.kubernetes.utils.DbUtils.startOracleDB; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.DomainUtils.deleteDomainResource; @@ -94,7 +93,7 @@ @DisplayName("Test to create a FMW domain in persistent volume with new simplified feature") @IntegrationTest @Tag("kind-sequential") -@Tag("oke-sequential1") +@Tag("oke-gate") @Tag("okd-fmw-cert") class ItFmwDomainOnPV { @@ -144,13 +143,18 @@ public static void initAll(@Namespaces(3) List namespaces) { DOMAINHOMEPREFIX = "/shared/" + domainNamespace + "/domains/"; - // start DB + //install Oracle Database Operator + String dbName = "fmwdomainonpv1" + "my-oracle-db"; + logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); + /* logger.info("Start DB in namespace: {0}, dbListenerPort: {1}, dbUrl: {2}, dbImage: {3}", dbNamespace, dbListenerPort, dbUrl, DB_IMAGE_TO_USE_IN_SPEC); assertDoesNotThrow(() -> startOracleDB(DB_IMAGE_TO_USE_IN_SPEC, getNextFreePort(), dbNamespace, dbListenerPort), String.format("Failed to start Oracle DB in the namespace %s with dbUrl %s, dbListenerPost %s", dbNamespace, dbUrl, dbListenerPort)); - + */ // install operator and verify its running in ready state HelmParams opHelmParams = new HelmParams().releaseName(OPERATOR_RELEASE_NAME) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java index 492fdac3b11..f0cb972f631 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java @@ -9,7 +9,9 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import io.kubernetes.client.openapi.models.V1Container; @@ -25,6 +27,7 @@ import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.NginxParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -40,11 +43,13 @@ import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.impl.primitive.Image.getImageEnvVar; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapForDomainCreation; import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; @@ -55,6 +60,8 @@ import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyEMconsoleAccess; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createIngressForDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; @@ -71,7 +78,7 @@ */ @DisplayName("Test to creat a FMW dynamic domain in persistent volume using WLST") @IntegrationTest -@Tag("oke-sequential") +@Tag("oke-gate") @Tag("kind-sequential") @Tag("okd-fmw-cert") class ItFmwDynamicDomainInPV { @@ -107,6 +114,13 @@ class ItFmwDynamicDomainInPV { private static String hostHeader; private static int adminPort = 7001; private static String dbName = domainUid + "my-oracle-db"; + private static String nginxNamespace = null; + private static List ingressHostList = null; + private static String ingressIP = null; + private static NginxParams nginxHelmParams = null; + + + /** * Assigns unique namespaces for DB, operator and domains. @@ -114,7 +128,7 @@ class ItFmwDynamicDomainInPV { * Pull FMW image and Oracle DB image if running tests in Kind cluster. */ @BeforeAll - public static void initAll(@Namespaces(3) List namespaces) { + public static void initAll(@Namespaces(4) List namespaces) { logger = getLogger(); logger.info("Assign a unique namespace for DB and RCU"); @@ -129,6 +143,11 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace is null"); domainNamespace = namespaces.get(2); + // get a unique Nginx namespace + logger.info("Assign a unique namespace for Nginx"); + assertNotNull(namespaces.get(3), "Namespace list is null"); + nginxNamespace = namespaces.get(3); + logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); createBaseRepoSecret(dbNamespace); dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); @@ -141,6 +160,13 @@ public static void initAll(@Namespaces(3) List namespaces) { // install operator and verify its running in ready state installAndVerifyOperator(opNamespace, domainNamespace); + if (OKE_CLUSTER_PRIVATEIP) { + // install Nginx ingress controller for all test cases using Nginx + nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + ingressIP = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace); + } + } /** @@ -156,10 +182,21 @@ void testFmwDynamicDomainInPV() { verifyDomainReady(domainNamespace, domainUid, replicaCount, "nosuffix"); // Expose the admin service external node port as a route for OKD adminSvcExtHost = createRouteForOKD(getExternalServicePodName(adminServerPodName), domainNamespace); + if (OKE_CLUSTER_PRIVATEIP) { + Map clusterNameMsPortMap = new HashMap<>(); + clusterNameMsPortMap.put(clusterName, managedServerPort); + ingressHostList + = createIngressForDomainAndVerify(domainUid, domainNamespace, 0, clusterNameMsPortMap, + false, nginxHelmParams.getIngressClassName(), true, 7001); + } + if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostHeader = createIngressHostRouting(domainNamespace, domainUid, adminServerName, adminPort); verifyEMconsoleAccess(domainNamespace, domainUid, adminSvcExtHost, hostHeader); + } else if (OKE_CLUSTER_PRIVATEIP) { + hostHeader = " -H 'host: " + ingressHostList.get(0) + "' "; + verifyEMconsoleAccess(domainNamespace, domainUid, ingressIP, hostHeader); } else { verifyEMconsoleAccess(domainNamespace, domainUid, adminSvcExtHost); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index 3a846053a2a..0455d203c53 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -1065,10 +1065,20 @@ public static boolean checkApplicationRuntime( expectedStatusCode); } - private static String readRuntimeResource(String adminSvcExtHost, String domainNamespace, - String adminServerPodName, String resourcePath, String callerName) { + /** + * Use REST APIs to check the application runtime mbean from the WebLogic server. + * + * @param adminSvcExtHost Used only in OKD env - this is the route host created for AS external service + * @param domainNamespace Kubernetes namespace that the domain is hosted + * @param adminServerPodName Name of the admin server pod to which the REST requests should be sent to + * @param resourcePath resource path + * @param callerName caller name + * @return the expected response + */ + public static String readRuntimeResource(String adminSvcExtHost, String domainNamespace, + String adminServerPodName, String resourcePath, String callerName) { LoggingFacade logger = getLogger(); - String returnString = null; + String returnString = ""; if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { @@ -1095,10 +1105,12 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN HttpResponse response; try { response = OracleHttpClient.get(url, headers, true); - assertEquals(200, response.statusCode()); returnString = response.body(); + assertEquals(200, response.statusCode()); } catch (Exception ex) { ex.printStackTrace(); + } catch (AssertionError e) { + e.printStackTrace(); } } else { String curlString; @@ -1116,10 +1128,7 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN } else { int adminServiceNodePort = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); - String host = K8S_NODEPORT_HOST; - if (host.contains(":")) { - host = "[" + host + "]"; - } + String host = formatIPv6Host(K8S_NODEPORT_HOST); String hostAndPort = (OKD) ? adminSvcExtHost : host + ":" + adminServiceNodePort; logger.info("hostAndPort = {0} ", hostAndPort); @@ -1128,9 +1137,9 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN + ":" + ADMIN_PASSWORD_DEFAULT + " http://%s%s/ --silent --show-error ", hostAndPort, resourcePath); - } - logger.info(callerName + ": curl command {0}", curlString); + logger.info(callerName + ": curl command {0}", curlString); + } try { String result = exec(curlString, true).stdout(); logger.info(callerName + ": exec curl command {0} got: {1}", curlString, result); @@ -1139,7 +1148,6 @@ private static String readRuntimeResource(String adminSvcExtHost, String domainN logger.info(callerName + ": caught unexpected exception {0}", ex); } } - return returnString; } @@ -1237,8 +1245,7 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost, curlString = new StringBuffer("status=$(curl -g --user weblogic:welcome1 http://"); } - String host = K8S_NODEPORT_HOST; - formatIPv6Host(host); + String host = formatIPv6Host(K8S_NODEPORT_HOST); String hostAndPort = (OKD) ? adminSvcExtHost : host + ":" + adminServiceNodePort; logger.info("hostAndPort = {0} ", hostAndPort); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index fa30c49b4a9..1c49d9c1003 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -69,6 +69,7 @@ import static oracle.weblogic.kubernetes.TestConstants.NO_PROXY; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; @@ -1165,10 +1166,7 @@ public static void verifyServerCommunication(String curlRequest, List ma logger.severe(ex.getMessage()); } - String response = ""; - if (result.stdout() != null) { - response = result.stdout().trim(); - } + String response = result != null ? result.stdout().trim() : "result is null"; logger.info(response); for (var managedServer : managedServers.entrySet()) { boolean connectToOthers = true; @@ -1340,6 +1338,9 @@ public static String getHostAndPort(String hostName, int servicePort) { host = formatIPv6Host(host); String hostAndPort = ((OKD) ? hostName : host + ":" + servicePort); logger.info("hostAndPort = {0} ", hostAndPort); + if (OKE_CLUSTER_PRIVATEIP) { + hostAndPort = hostName; + } return hostAndPort; } catch (UnknownHostException e) { throw new RuntimeException(e); From 1931802f4c898cc205bb99f7d7a9b6ef9748a7fe Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 8 Jul 2024 19:59:05 +0000 Subject: [PATCH 099/356] Cherry-pick branch 'startup-probes' into 'release/4.2' --- documentation/domains/Cluster.json | 4 + documentation/domains/Cluster.md | 1 + documentation/domains/Domain.json | 4 + documentation/domains/Domain.md | 1 + .../domain-lifecycle/startup.md | 1 + kubernetes/crd/cluster-crd.yaml | 70 +++++- kubernetes/crd/domain-crd.yaml | 206 +++++++++++++++++- .../operator/helpers/PodStepContext.java | 7 +- .../processing/EffectiveServerSpec.java | 2 + .../domain/model/BaseConfiguration.java | 8 +- .../model/EffectiveServerSpecCommonImpl.java | 5 + .../weblogic/domain/model/ServerPod.java | 25 ++- 12 files changed, 327 insertions(+), 7 deletions(-) diff --git a/documentation/domains/Cluster.json b/documentation/domains/Cluster.json index 4bbd509d605..556206bdec3 100644 --- a/documentation/domains/Cluster.json +++ b/documentation/domains/Cluster.json @@ -205,6 +205,10 @@ "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.HostAlias" } }, + "startupProbe": { + "description": "Settings for the startup probe associated with a WebLogic Server instance. If not specified, the operator will not create a default startup probe.", + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe" + }, "nodeSelector": { "description": "Selector which must match a Node\u0027s labels for the Pod to be scheduled on that Node. See `kubectl explain pods.spec.nodeSelector`.", "additionalProperties": { diff --git a/documentation/domains/Cluster.md b/documentation/domains/Cluster.md index bc8173ac90d..43bb35d7b99 100644 --- a/documentation/domains/Cluster.md +++ b/documentation/domains/Cluster.md @@ -77,6 +77,7 @@ The specification of the operation of the WebLogic cluster. Required. | `schedulerName` | string | If specified, the Pod will be dispatched by the specified scheduler. If not specified, the Pod will be dispatched by the default scheduler. See `kubectl explain pods.spec.schedulerName`. | | `serviceAccountName` | string | Name of the ServiceAccount to be used to run this Pod. If it is not set, default ServiceAccount will be used. The ServiceAccount has to exist at the time the Pod is created. See `kubectl explain pods.spec.serviceAccountName`. | | `shutdown` | [Shutdown](#shutdown) | Configures how the operator should shut down the server instance. | +| `startupProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the startup probe associated with a WebLogic Server instance. If not specified, the operator will not create a default startup probe. | | `tolerations` | Array of [Toleration](k8s1.28.2.md#toleration) | If specified, the Pod's tolerations. See `kubectl explain pods.spec.tolerations`. | | `topologySpreadConstraints` | Array of [Topology Spread Constraint](k8s1.28.2.md#topology-spread-constraint) | TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. | | `volumeMounts` | Array of [Volume Mount](k8s1.28.2.md#volume-mount) | Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`. | diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index 6e52837c257..fbb59bbadf8 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -1066,6 +1066,10 @@ "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.HostAlias" } }, + "startupProbe": { + "description": "Settings for the startup probe associated with a WebLogic Server instance. If not specified, the operator will not create a default startup probe.", + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe" + }, "nodeSelector": { "description": "Selector which must match a Node\u0027s labels for the Pod to be scheduled on that Node. See `kubectl explain pods.spec.nodeSelector`.", "additionalProperties": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 69353f18487..860a75555e6 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -178,6 +178,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | `schedulerName` | string | If specified, the Pod will be dispatched by the specified scheduler. If not specified, the Pod will be dispatched by the default scheduler. See `kubectl explain pods.spec.schedulerName`. | | `serviceAccountName` | string | Name of the ServiceAccount to be used to run this Pod. If it is not set, default ServiceAccount will be used. The ServiceAccount has to exist at the time the Pod is created. See `kubectl explain pods.spec.serviceAccountName`. | | `shutdown` | [Shutdown](#shutdown) | Configures how the operator should shut down the server instance. | +| `startupProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the startup probe associated with a WebLogic Server instance. If not specified, the operator will not create a default startup probe. | | `tolerations` | Array of [Toleration](k8s1.28.2.md#toleration) | If specified, the Pod's tolerations. See `kubectl explain pods.spec.tolerations`. | | `topologySpreadConstraints` | Array of [Topology Spread Constraint](k8s1.28.2.md#topology-spread-constraint) | TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed. | | `volumeMounts` | Array of [Volume Mount](k8s1.28.2.md#volume-mount) | Additional volume mounts for the container running a WebLogic Server instance. See `kubectl explain pods.spec.containers.volumeMounts`. | diff --git a/documentation/site/content/managing-domains/domain-lifecycle/startup.md b/documentation/site/content/managing-domains/domain-lifecycle/startup.md index 0c13789e942..16f39c2a10d 100644 --- a/documentation/site/content/managing-domains/domain-lifecycle/startup.md +++ b/documentation/site/content/managing-domains/domain-lifecycle/startup.md @@ -287,6 +287,7 @@ The operator will restart servers when any of the follow fields on the Domain th * `readinessProbe` * `resources` * `restartVersion` +* `startupProbe` * `volumes` * `volumeMounts` diff --git a/kubernetes/crd/cluster-crd.yaml b/kubernetes/crd/cluster-crd.yaml index 96e1c5b11b5..aea21d23ed4 100644 --- a/kubernetes/crd/cluster-crd.yaml +++ b/kubernetes/crd/cluster-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 25a60de9429ea0f9bad45b3083d18c9ff56c86c62a123cdff67af4bacf82eec5 + weblogic.sha256: 2f9c1cd88595595fecef563dbc9a149f318eab7146b63cb6142c45279ad4f9e7 name: clusters.weblogic.oracle spec: group: weblogic.oracle @@ -207,6 +207,74 @@ spec: items: type: string type: array + startupProbe: + description: Settings for the startup probe associated with a + WebLogic Server instance. If not specified, the operator will + not create a default startup probe. + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object + type: object nodeSelector: additionalProperties: type: string diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 7f9557e629b..239a5314da6 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: ee0c6f79288b000cd61aa9438ab7f24f63b6334072e3520f25ee454f8bff6175 + weblogic.sha256: b48d0f17054d017bd3d9df2d30929bab24882235a628b715f59092f288dd569e name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -1301,6 +1301,74 @@ spec: items: type: string type: array + startupProbe: + description: Settings for the startup probe associated with + a WebLogic Server instance. If not specified, the operator + will not create a default startup probe. + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object + type: object nodeSelector: additionalProperties: type: string @@ -4482,6 +4550,74 @@ spec: items: type: string type: array + startupProbe: + description: Settings for the startup probe associated with a + WebLogic Server instance. If not specified, the operator will + not create a default startup probe. + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + type: object + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + properties: + command: + items: + type: string + type: array + type: object + grpc: + properties: + port: + type: integer + service: + type: string + required: + - port + type: object + httpGet: + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: array + required: + - port + type: object + type: object nodeSelector: additionalProperties: type: string @@ -7303,6 +7439,74 @@ spec: type: array items: type: string + startupProbe: + description: Settings for the startup probe associated with + a WebLogic Server instance. If not specified, the operator + will not create a default startup probe. + type: object + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + type: object + properties: + command: + type: array + items: + type: string + grpc: + type: object + properties: + port: + type: integer + service: + type: string + required: + - port + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port nodeSelector: description: Selector which must match a Node's labels for the Pod to be scheduled on that Node. See `kubectl explain diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index b33c61de4c1..d0ee512d30c 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -752,7 +752,8 @@ protected V1Container createPrimaryContainer() { V1Container v1Container = super.createPrimaryContainer() .ports(getContainerPorts()) .lifecycle(createLifecycle()) - .livenessProbe(createLivenessProbe(podTuning)); + .livenessProbe(createLivenessProbe(podTuning)) + .startupProbe(getStartupProbe()); if (!mockWls()) { v1Container.readinessProbe(createReadinessProbe(podTuning)); @@ -1003,6 +1004,10 @@ private V1Probe getLivenessProbe() { .map(V1ProbeBuilder::new).map(V1ProbeBuilder::build).orElse(new V1Probe()); } + private V1Probe getStartupProbe() { + return getServerSpec().getStartupProbe(); + } + private boolean mockWls() { return Boolean.getBoolean("mockWLS"); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java index 6acc8c009e8..405fbbe5d2e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java +++ b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java @@ -93,6 +93,8 @@ public interface EffectiveServerSpec { @Nonnull V1Probe getReadinessProbe(); + V1Probe getStartupProbe(); + @Nonnull Shutdown getShutdown(); diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java index cee13da3b04..0168fe1787a 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java @@ -148,7 +148,7 @@ public void setLivenessProbeThresholds(Integer successThreshold, Integer failure } V1Probe getLivenessProbe() { - return serverPod.getLivenessProbeTuning(); + return serverPod.getLivenessProbe(); } void setReadinessProbe(Integer initialDelay, Integer timeout, Integer period) { @@ -164,7 +164,11 @@ void setReadinessProbeHttpGetActionPath(String httpGetActionPath) { } V1Probe getReadinessProbe() { - return serverPod.getReadinessProbeTuning(); + return serverPod.getReadinessProbe(); + } + + V1Probe getStartupProbe() { + return serverPod.getStartupProbe(); } Shutdown getShutdown() { diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java index 2de1c683491..d6e6b662451 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java @@ -163,6 +163,11 @@ public V1Probe getReadinessProbe() { return server.getReadinessProbe(); } + @Override + public V1Probe getStartupProbe() { + return server.getStartupProbe(); + } + @Nonnull @Override public Shutdown getShutdown() { diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java index 13ea791fe95..80b9bf2d1d7 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java @@ -108,6 +108,15 @@ class ServerPod extends KubernetesResource { + " defines a different action, such as a different HTTP path to access.") private V1Probe readinessProbe = null; + /** + * Defines the settings for a startup probe. + * + * @since 4.2.4 + */ + @Description("Settings for the startup probe associated with a WebLogic Server instance." + + " If not specified, the operator will not create a default startup probe.") + private V1Probe startupProbe = null; + /** * Defines the key-value pairs for the pod to fit on a node, the node must have each of the * indicated key-value pairs as labels. @@ -411,7 +420,7 @@ void setShutdown(ShutdownType shutdownType, Long timeoutSeconds, Boolean ignoreS .skipWaitingCohEndangeredState(skipWaitingCohEndangeredState); } - V1Probe getReadinessProbeTuning() { + V1Probe getReadinessProbe() { return this.readinessProbe; } @@ -433,7 +442,7 @@ void setReadinessProbeHttpGetActionPath(String httpGetActionPath) { .httpGet(new V1HTTPGetAction().path(httpGetActionPath)); } - V1Probe getLivenessProbeTuning() { + V1Probe getLivenessProbe() { return this.livenessProbe; } @@ -450,6 +459,10 @@ void setLivenessProbeThresholds(Integer successThreshold, Integer failureThresho .failureThreshold(failureThreshold); } + V1Probe getStartupProbe() { + return startupProbe; + } + public Long getMaxReadyWaitTimeSeconds() { return this.maxReadyWaitTimeSeconds; } @@ -486,6 +499,11 @@ void fillInFrom(ServerPod serverPod1) { } else { copyValues(readinessProbe, serverPod1.readinessProbe); } + if (startupProbe == null) { + startupProbe = serverPod1.startupProbe; + } else { + copyValues(startupProbe, serverPod1.startupProbe); + } shutdown.copyValues(serverPod1.shutdown); for (V1Volume volume : serverPod1.getAdditionalVolumes()) { addIfMissing(new V1VolumeBuilder(volume).build()); @@ -855,6 +873,7 @@ public String toString() { .append("envFrom", envFrom) .append("livenessProbe", livenessProbe) .append("readinessProbe", readinessProbe) + .append("startupProbe", startupProbe) .append("additionalVolumes", volumes) .append("additionalVolumeMounts", volumeMounts) .append("nodeSelector", nodeSelector) @@ -898,6 +917,7 @@ public boolean equals(Object o) { .append(envFrom, that.envFrom) .append(livenessProbe, that.livenessProbe) .append(readinessProbe, that.readinessProbe) + .append(startupProbe, that.startupProbe) .append( DomainResource.sortList(volumes, VOLUME_COMPARATOR), DomainResource.sortList(that.volumes, VOLUME_COMPARATOR)) @@ -933,6 +953,7 @@ public int hashCode() { .append(envFrom) .append(livenessProbe) .append(readinessProbe) + .append(startupProbe) .append(DomainResource.sortList(volumes, VOLUME_COMPARATOR)) .append(DomainResource.sortList(volumeMounts, VOLUME_MOUNT_COMPARATOR)) .append(nodeSelector) From 16f9c56447e6dc7cccc5314c76ed2a2053b5520c Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 9 Jul 2024 00:28:21 +0000 Subject: [PATCH 100/356] use existing repos for adding images --- .../ItConfigDistributionStrategy.java | 14 +++-- .../kubernetes/ItIntrospectVersion.java | 58 +++---------------- .../kubernetes/ItKubernetesDomainEvents.java | 11 ++-- .../kubernetes/ItSystemResOverrides.java | 11 ++-- .../weblogic/kubernetes/ItT3Channel.java | 11 ++-- .../kubernetes/utils/DomainUtils.java | 46 ++++++++++++++- .../weblogic/kubernetes/utils/FmwUtils.java | 13 ++++- 7 files changed, 88 insertions(+), 76 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index 99d482861f6..4ab3709ecbb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -64,7 +64,6 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; -import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; @@ -92,6 +91,7 @@ import static oracle.weblogic.kubernetes.utils.BuildApplication.buildApplication; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; @@ -986,18 +986,19 @@ private void createDomain() { File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage - String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "configdist-domain-on-pv-image"; + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; + String domainCreationImagetag = getDateAndTimeStamp(); // create image with model and wdt installation files WitParams witParams = new WitParams() .modelImageName(domainCreationImageName) - .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelImageTag(domainCreationImagetag) .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); - createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + createAndPushAuxiliaryImage(domainCreationImageName, domainCreationImagetag, witParams); DomainCreationImage domainCreationImage - = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + = new DomainCreationImage().image(domainCreationImageName + ":" + domainCreationImagetag); // create a domain resource logger.info("Creating domain custom resource"); @@ -1030,7 +1031,8 @@ private void createDomain() { uniqueDomainHome, 2, t3ChannelPort, - configuration); + configuration, + WEBLOGIC_IMAGE_TO_USE_IN_SPEC); domain.spec().serverPod().addEnvItem(new V1EnvVar() .name("JAVA_OPTIONS") .value("-Dweblogic.debug.DebugSituationalConfig=true " diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index c053ba82497..10499a021c1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -23,7 +23,6 @@ import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiException; -import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1Ingress; import io.kubernetes.client.openapi.models.V1LocalObjectReference; @@ -121,6 +120,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.generateNewModelFileWithUpdatedDomainUid; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; @@ -129,7 +129,6 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyServerCommunication; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; -import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapForDomainCreation; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeExists; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeHasExpectedStatus; @@ -140,7 +139,6 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createMiiImageAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; -import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; import static oracle.weblogic.kubernetes.utils.K8sEvents.DOMAIN_ROLL_COMPLETED; import static oracle.weblogic.kubernetes.utils.K8sEvents.DOMAIN_ROLL_STARTING; @@ -1152,18 +1150,19 @@ private static void createDomain() { File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage - String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "introspect-domain-on-pv-image"; + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; + String domainCreationImagetag = getDateAndTimeStamp(); // create image with model and wdt installation files WitParams witParams = new WitParams() .modelImageName(domainCreationImageName) - .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelImageTag(domainCreationImagetag) .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); - createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + createAndPushAuxiliaryImage(domainCreationImageName, domainCreationImagetag, witParams); DomainCreationImage domainCreationImage - = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + = new DomainCreationImage().image(domainCreationImageName + ":" + domainCreationImagetag); // create a domain resource logger.info("Creating domain custom resource"); @@ -1194,7 +1193,8 @@ private static void createDomain() { uniqueDomainHome, cluster1ReplicaCount, t3ChannelPort, - configuration); + configuration, + WEBLOGIC_IMAGE_TO_USE_IN_SPEC); setPodAntiAffinity(domain); // verify the domain custom resource is created @@ -1253,48 +1253,6 @@ private static void createDomain() { verifyMemberHealth(adminServerPodName, managedServerNames, wlsUserName, wlsPassword); } - - /** - * Create a WebLogic domain on a persistent volume by doing the following. - * Create a configmap containing WLST script and property file. - * Create a Kubernetes job to create domain on persistent volume. - * - * @param wlstScriptFile python script to create domain - * @param domainPropertiesFile properties file containing domain configuration - * @param pvName name of the persistent volume to create domain in - * @param pvcName name of the persistent volume claim - * @param namespace name of the domain namespace in which the job is created - */ - private static void createDomainOnPVUsingWlst(Path wlstScriptFile, Path domainPropertiesFile, - String pvName, String pvcName, String namespace) { - logger.info("Preparing to run create domain job using WLST"); - - List domainScriptFiles = new ArrayList<>(); - domainScriptFiles.add(wlstScriptFile); - domainScriptFiles.add(domainPropertiesFile); - - logger.info("Creating a config map to hold domain creation scripts"); - String domainScriptConfigMapName = "create-domain-scripts-cm"; - assertDoesNotThrow( - () -> createConfigMapForDomainCreation( - domainScriptConfigMapName, domainScriptFiles, namespace, ItIntrospectVersion.class.getSimpleName()), - "Create configmap for domain creation failed"); - - // create a V1Container with specific scripts and properties for creating domain - V1Container jobCreationContainer = new V1Container() - .addCommandItem("/bin/sh") - .addArgsItem("/u01/oracle/oracle_common/common/bin/wlst.sh") - .addArgsItem("/u01/weblogic/" + wlstScriptFile.getFileName()) //wlst.sh script - .addArgsItem("-skipWLSModuleScanning") - .addArgsItem("-loadProperties") - .addArgsItem("/u01/weblogic/" + domainPropertiesFile.getFileName()); //domain property file - - logger.info("Running a Kubernetes job to create the domain"); - createDomainJob(WEBLOGIC_IMAGE_TO_USE_IN_SPEC, pvName, pvcName, domainScriptConfigMapName, - namespace, jobCreationContainer); - - } - private static void verifyMemberHealth(String adminServerPodName, List managedServerNames, String user, String code) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index baa929582ab..a76bd8252d3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -59,7 +59,6 @@ import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; -import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; @@ -85,6 +84,7 @@ import static oracle.weblogic.kubernetes.utils.ClusterUtils.removeReplicasSettingAndVerify; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; @@ -776,18 +776,19 @@ private static DomainResource createDomain(String domainNamespace, String domain K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage - String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "events-domain-on-pv-image"; + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; + String domainCreationImagetag = getDateAndTimeStamp(); // create image with model and wdt installation files WitParams witParams = new WitParams() .modelImageName(domainCreationImageName) - .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelImageTag(domainCreationImagetag) .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); - createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + createAndPushAuxiliaryImage(domainCreationImageName, domainCreationImagetag, witParams); DomainCreationImage domainCreationImage - = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + = new DomainCreationImage().image(domainCreationImageName + ":" + domainCreationImagetag); // create a domain resource logger.info("Creating domain custom resource"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index 4f2e9cb4952..5f5cfc86b83 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -58,7 +58,6 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; -import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -81,6 +80,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.exeAppInServerPod; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; @@ -397,18 +397,19 @@ private void createDomain() { K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage - String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "sitconfig-domain-on-pv-image"; + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; + String domainCreationImagetag = getDateAndTimeStamp(); // create image with model and wdt installation files WitParams witParams = new WitParams() .modelImageName(domainCreationImageName) - .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelImageTag(domainCreationImagetag) .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); - createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + createAndPushAuxiliaryImage(domainCreationImageName, domainCreationImagetag, witParams); DomainCreationImage domainCreationImage - = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + = new DomainCreationImage().image(domainCreationImageName + ":" + domainCreationImagetag); // create a domain resource logger.info("Creating domain custom resource"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 89c33972845..e1ee2b920aa 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -61,7 +61,6 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; -import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -75,6 +74,7 @@ import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; @@ -178,18 +178,19 @@ void testAdminServerT3Channel() { K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage - String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "t3channel-domain-on-pv-image"; + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; + String domainCreationImagetag = getDateAndTimeStamp(); // create image with model and wdt installation files WitParams witParams = new WitParams() .modelImageName(domainCreationImageName) - .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelImageTag(domainCreationImagetag) .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); - createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + createAndPushAuxiliaryImage(domainCreationImageName, domainCreationImagetag, witParams); DomainCreationImage domainCreationImage - = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + = new DomainCreationImage().image(domainCreationImageName + ":" + domainCreationImagetag); // create a domain resource logger.info("Creating domain custom resource"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java index 16975d25cdd..e8636c2d127 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java @@ -968,7 +968,7 @@ public static DomainResource createDomainResourceForDomainOnPV(String domainUid, } /** - * Utility to create domain resource on pv with confiiguration. + * Utility to create domain resource on pv with configuration. * @param domainUid domain uid * @param domNamespace domain namespace * @param adminSecretName wls admin secret name @@ -993,6 +993,48 @@ public static DomainResource createDomainResourceOnPv(String domainUid, int replicaCount, int t3ChannelPort, Configuration configuration) { + return createDomainResourceOnPv(domainUid, + domNamespace, + adminSecretName, + clusterName, + pvName, + pvcName, + repoSecretName, + domainInHomePrefix, + replicaCount, + t3ChannelPort, + configuration, + FMWINFRA_IMAGE_TO_USE_IN_SPEC); + } + + /** + * Utility to create domain resource on pv with configuration. + * @param domainUid domain uid + * @param domNamespace domain namespace + * @param adminSecretName wls admin secret name + * @param clusterName cluster name + * @param pvName PV name + * @param pvcName PVC name + * @param repoSecretName name of the secret for pulling the WebLogic image + * @param domainInHomePrefix domain in home prefix + * @param replicaCount repica count of the clsuter + * @param t3ChannelPort t3 chanel + * @param configuration domain configuratioin object + * @param imageToUse base image to use + * @return oracle.weblogic.domain.Domain object + */ + public static DomainResource createDomainResourceOnPv(String domainUid, + String domNamespace, + String adminSecretName, + String clusterName, + String pvName, + String pvcName, + String[] repoSecretName, + String domainInHomePrefix, + int replicaCount, + int t3ChannelPort, + Configuration configuration, + String imageToUse) { // create secrets List secrets = new ArrayList<>(); @@ -1011,7 +1053,7 @@ public static DomainResource createDomainResourceOnPv(String domainUid, .domainUid(domainUid) .domainHome(domainInHomePrefix + domainUid) .domainHomeSourceType("PersistentVolume") - .image(FMWINFRA_IMAGE_TO_USE_IN_SPEC) + .image(imageToUse) .imagePullPolicy(IMAGE_PULL_POLICY) .webLogicCredentialsSecret(new V1LocalObjectReference() .name(adminSecretName)) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java index 55ece386c9d..e18b6b35e70 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java @@ -14,6 +14,7 @@ import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1HostPathVolumeSource; import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1NFSVolumeSource; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; import io.kubernetes.client.openapi.models.V1ResourceRequirements; @@ -48,8 +49,10 @@ import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.NFS_SERVER; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.PV_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.YAML_MAX_FILE_SIZE_PROPERTY; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -674,8 +677,6 @@ public static Configuration getConfiguration(String pvName, String pvcName, } else if (OKD) { storageClassName = "okd-nfsmnt"; } - - pv = new PersistentVolume() .spec(new PersistentVolumeSpec() .capacity(pvCapacity) @@ -683,10 +684,16 @@ public static Configuration getConfiguration(String pvName, String pvcName, .persistentVolumeReclaimPolicy("Retain")) .metadata(new V1ObjectMeta() .name(pvName)); - if (!OKE_CLUSTER) { + if (!OKE_CLUSTER && !OKD) { pv.getSpec().hostPath(new V1HostPathVolumeSource() .path(getHostPath(pvName, testClass))); } + if (OKD) { + pv.getSpec().nfs(new V1NFSVolumeSource() + .path(PV_ROOT) + .server(NFS_SERVER) + .readOnly(false)); + } configuration .introspectorJobActiveDeadlineSeconds(3000L) .initializeDomainOnPV(new InitializeDomainOnPV() From f3756939bcdf4bfac25e4b2b424679d3793e94c0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 11 Jul 2024 22:21:39 +0000 Subject: [PATCH 101/356] Merge branch 'fix-eval-failure-for-large-model' into 'main' Use jq to get partial models for sanity check for online update forbidden... See merge request weblogic-cloud/weblogic-kubernetes-operator!4749 (cherry picked from commit 1a95e97477df2bae87cf54ec9eeca8d61f0b65c5) 907fbd6e Use jq to get partial models for sanity check for online update forbidden... 8f6ac082 correct logic requirements for model-diff 71a760d0 correct logic requirements for model-diff jq 3ae0a06b update logic of jq 82a0d279 update copyright --- .../resources/scripts/introspectDomain.sh | 6 ++- .../src/main/resources/scripts/model-diff.py | 42 +++++++++---------- .../main/resources/scripts/modelInImage.sh | 26 ++++++++++-- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/operator/src/main/resources/scripts/introspectDomain.sh b/operator/src/main/resources/scripts/introspectDomain.sh index d411e0ba5a0..1da3c2db52e 100755 --- a/operator/src/main/resources/scripts/introspectDomain.sh +++ b/operator/src/main/resources/scripts/introspectDomain.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -185,6 +185,10 @@ doIntrospect() { if [ $? -ne 0 ] ; then trace SEVERE "DomainSourceType is 'FromModel', 'unzip' is missing in the image. Please use an image with 'unzip' installed" && exit 1 fi + command -v jq + if [ $? -ne 0 ] ; then + trace SEVERE "DomainSourceType is 'FromModel' and 'onlineUpdate' is enabled, 'jq' is missing in the image. Please use an image with 'jq' installed" && exit 1 + fi createFolder "${DOMAIN_HOME}" "DomainSourceType is 'FromModel' and this is the DOMAIN_HOME directory specified by 'domain.spec.domainHome'." || exit 1 createWLDomain || exit 1 created_domain=$DOMAIN_CREATED diff --git a/operator/src/main/resources/scripts/model-diff.py b/operator/src/main/resources/scripts/model-diff.py index 49aa88277cb..0cdd3efa322 100644 --- a/operator/src/main/resources/scripts/model-diff.py +++ b/operator/src/main/resources/scripts/model-diff.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2022, Oracle and/or its affiliates. +# Copyright (c) 2019, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # # ------------ @@ -46,8 +46,9 @@ def is_not_safe_for_online_update(self, model, original_model): def is_safe_diff(self, model, original_model): """ - Is it a safe difference for update. + Is the difference safe for update. :param model: diffed model + :param original_model: original model (partial only contain server and template names) return 0 - always return 0 for V1 """ @@ -103,8 +104,10 @@ def in_forbidden_list(self, model, original_model): for key in [ 'Server', 'ServerTemplate']: # topology.Server|ServerTemplate if model[_TOPOLOGY].has_key(key): - temp = model[_TOPOLOGY][key] - for server in temp: + # topology.Server or topology.ServerTemplate + svr_list = model[_TOPOLOGY][key] + for server in svr_list: + # topology.Server.item or topology.ServerTemplate.item # cannot delete server or template if server.startswith('!'): return 1 @@ -112,16 +115,16 @@ def in_forbidden_list(self, model, original_model): if server not in original_model['topology'][key]: continue for not_this in forbidden_network_attributes: - if temp[server].has_key(not_this): + if svr_list[server].has_key(not_this): return 1 - if temp[server].has_key(_NAP): - nap = temp[server][_NAP] - for n in nap: + if svr_list[server].has_key(_NAP): + naps = svr_list[server][_NAP] + for nap in naps: for not_this in forbidden_network_attributes: - if temp[server].has_key(not_this): + if svr_list[server][_NAP][nap].has_key(not_this): return 1 # Do not allow any SSL changes - if temp[server].has_key(_SSL): + if svr_list[server].has_key(_SSL): return 1 return 0 @@ -129,22 +132,15 @@ def in_forbidden_list(self, model, original_model): class ModelFileDiffer: - def eval_file(self, file): - true = True - false = False - fh = open(file, 'r') - content = fh.read() - return eval(content) + def eval_string(self, string): + return eval(string) def compare(self): - original_model = self.eval_file(sys.argv[1]) - # past_dict = self.eval_file(sys.argv[2]) + original_topology = self.eval_string(sys.argv[1]) + net_diff = self.eval_string(sys.argv[2]) obj = ModelDiffer() - if os.path.exists('/tmp/diffed_model.json'): - net_diff = self.eval_file('/tmp/diffed_model.json') - else: - net_diff = {} - return obj.is_safe_diff(net_diff, original_model) + return obj.is_safe_diff(net_diff, original_topology) + def debug(format_string, *arguments): if os.environ.has_key('DEBUG_INTROSPECT_JOB'): diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 4a794450f33..0c97236cf82 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -587,8 +587,8 @@ diff_model() { trace "Entering diff_model" # wdt shell script or logFileRotate may return non-zero code if trap is on, then it will go to trap instead # temporarily disable it - stop_trap + stop_trap export __WLSDEPLOY_STORE_MODEL__=1 # $1 - new model, $2 original model @@ -620,17 +620,36 @@ diff_model() { fi fi + # Checking whether domain, rcu credentials have been changed - needed for offline + # and also incompatible changes for online update. + if [ "${MERGED_MODEL_ENVVARS_SAME}" == "false" ] ; then - # Generate diffed model update compatibility result + # Generate diffed model update compatibility result, use partial model to avoid loading large model local ORACLE_SERVER_DIR=${ORACLE_HOME}/wlserver local JAVA_PROPS="-Dpython.cachedir.skip=true ${JAVA_PROPS}" local JAVA_PROPS="-Dpython.path=${ORACLE_SERVER_DIR}/common/wlst/modules/jython-modules.jar/Lib ${JAVA_PROPS}" local JAVA_PROPS="-Dpython.console= ${JAVA_PROPS} -Djava.security.egd=file:/dev/./urandom" local CP=${ORACLE_SERVER_DIR}/server/lib/weblogic.jar + # Get partial models for sanity check for forbidden attribute change + local SERVER_OR_SERVERTEMPLATES_NAMES=$(jq '{ topology: { Server: (.topology.Server | with_entries(.value = {})), + ServerTemplate: (if .topology.ServerTemplate then (.topology.ServerTemplate | with_entries(.value = {})) else empty end) + }} | if .topology.ServerTemplate == {} then del(.topology.ServerTemplate) else . end' $2) + rc=$? + if [ $rc -ne 0 ] ; then + trace SEVERE "Failed to extract server names from original model using jq "$rc + exitOrLoop + fi + local PARTIAL_DIFFED_MODEL=$(jq '{domainInfo: .domainInfo, topology: .topology} | with_entries(select(.value != null))' /tmp/diffed_model.json) + rc=$? + if [ $rc -ne 0 ] ; then + trace SEVERE "Failed to extract domainInfo and topology from delta model using jq "$rc + exitOrLoop + fi + ${JAVA_HOME}/bin/java -cp ${CP} \ ${JAVA_PROPS} \ org.python.util.jython \ - ${SCRIPTPATH}/model-diff.py $2 > ${WDT_OUTPUT} 2>&1 + ${SCRIPTPATH}/model-diff.py "$SERVER_OR_SERVERTEMPLATES_NAMES" "$PARTIAL_DIFFED_MODEL" > ${WDT_OUTPUT} 2>&1 if [ $? -ne 0 ] ; then trace SEVERE "Failed to compare models. Error output:" cat ${WDT_OUTPUT} @@ -640,7 +659,6 @@ diff_model() { wdtRotateAndCopyLogFile "${WDT_COMPARE_MODEL_LOG}" - # restore trap start_trap trace "Exiting diff_model" From c1c8b1ed1156000f0f3e1c4dcfd53b3996765227 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 9 Jul 2024 18:16:05 -0400 Subject: [PATCH 102/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 60b3013e938..a81a0cc9367 100644 --- a/pom.xml +++ b/pom.xml @@ -671,11 +671,11 @@ 3.3.1 3.12.1 3.2.5 - 3.3.1 - 3.0.1 + 3.4.0 + 3.1.0 3.6.3 3.2.5 - 3.6.1 + 3.7.1 3.6.0 3.3.0 10.17.0 @@ -702,7 +702,7 @@ 4.2.1 19.0.1 3.0.1u2 - 1.9.24 + 2.0.0 4.12.0 3.9.0 1.78.1 From 92ebe12ffc487a14f13117d4802c3adf4d164e17 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 11 Jul 2024 19:18:16 -0400 Subject: [PATCH 103/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index e97b847b6c7..cb5503d4f4b 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -24,7 +24,7 @@ org.apache.maven maven-plugin-api - 3.9.7 + 3.9.8 provided diff --git a/pom.xml b/pom.xml index a81a0cc9367..a3af1513c77 100644 --- a/pom.xml +++ b/pom.xml @@ -673,7 +673,7 @@ 3.2.5 3.4.0 3.1.0 - 3.6.3 + 3.7.0 3.2.5 3.7.1 3.6.0 @@ -714,7 +714,7 @@ 3.1.7 1.1.6 4.0.2 - 6.0.0 + 6.1.0 0.16.0 2.17.1 2.17.1 From 86c879a57e0c97472fd5202c49dc181c17336246 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 12 Jul 2024 11:05:53 -0400 Subject: [PATCH 104/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a3af1513c77..0ae3a4d02ed 100644 --- a/pom.xml +++ b/pom.xml @@ -670,11 +670,11 @@ 3.4.1 3.3.1 3.12.1 - 3.2.5 + 3.3.1 3.4.0 3.1.0 3.7.0 - 3.2.5 + 3.3.1 3.7.1 3.6.0 3.3.0 @@ -706,7 +706,7 @@ 4.12.0 3.9.0 1.78.1 - 5.10.2 + 5.10.3 5.7.1 1.7.0 1.3.2 @@ -716,8 +716,8 @@ 4.0.2 6.1.0 0.16.0 - 2.17.1 - 2.17.1 + 2.17.2 + 2.17.2 2.2 2.11.0 9.2.0 From 3938ce1e24057b347e38f9816d9cbce85f7b574a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 12 Jul 2024 18:40:03 -0400 Subject: [PATCH 105/356] Backport changes to operator scripts --- .../resources/scripts/createDomainOnPV.sh | 2 +- .../main/resources/scripts/livenessProbe.sh | 1 - .../resources/scripts/mii-domain-upgrade.py | 2 +- .../resources/scripts/model_wdt_mii_filter.py | 2 +- .../src/main/resources/scripts/monitorLog.sh | 3 +- .../src/main/resources/scripts/readState.sh | 3 +- .../resources/scripts/startNodeManager.sh | 2 +- .../src/main/resources/scripts/startServer.sh | 3 +- .../src/main/resources/scripts/stopServer.sh | 43 +++++++++++++------ .../src/main/resources/scripts/tailLog.sh | 3 +- .../src/main/resources/scripts/utils_base.sh | 3 +- .../src/main/resources/scripts/wdt_common.sh | 2 +- 12 files changed, 40 insertions(+), 29 deletions(-) diff --git a/operator/src/main/resources/scripts/createDomainOnPV.sh b/operator/src/main/resources/scripts/createDomainOnPV.sh index 04ed1f49a69..45fb5619e76 100644 --- a/operator/src/main/resources/scripts/createDomainOnPV.sh +++ b/operator/src/main/resources/scripts/createDomainOnPV.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # # This script contains the all the function of creating domain on pv diff --git a/operator/src/main/resources/scripts/livenessProbe.sh b/operator/src/main/resources/scripts/livenessProbe.sh index efc0d6538c6..33ceb730669 100755 --- a/operator/src/main/resources/scripts/livenessProbe.sh +++ b/operator/src/main/resources/scripts/livenessProbe.sh @@ -1,5 +1,4 @@ #!/bin/bash - # Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. diff --git a/operator/src/main/resources/scripts/mii-domain-upgrade.py b/operator/src/main/resources/scripts/mii-domain-upgrade.py index a30de9b90a1..9b69cc6d437 100644 --- a/operator/src/main/resources/scripts/mii-domain-upgrade.py +++ b/operator/src/main/resources/scripts/mii-domain-upgrade.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # This script is run during introspection to detect if secure production mode is enabled in diff --git a/operator/src/main/resources/scripts/model_wdt_mii_filter.py b/operator/src/main/resources/scripts/model_wdt_mii_filter.py index 8fb210a5163..c2b651084e3 100644 --- a/operator/src/main/resources/scripts/model_wdt_mii_filter.py +++ b/operator/src/main/resources/scripts/model_wdt_mii_filter.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # # ------------ diff --git a/operator/src/main/resources/scripts/monitorLog.sh b/operator/src/main/resources/scripts/monitorLog.sh index 22120c8fa52..4289de7591b 100755 --- a/operator/src/main/resources/scripts/monitorLog.sh +++ b/operator/src/main/resources/scripts/monitorLog.sh @@ -1,6 +1,5 @@ #!/bin/bash - -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. +# Copyright (c) 2019, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # diff --git a/operator/src/main/resources/scripts/readState.sh b/operator/src/main/resources/scripts/readState.sh index 8d769f51b72..b27bd9454a7 100755 --- a/operator/src/main/resources/scripts/readState.sh +++ b/operator/src/main/resources/scripts/readState.sh @@ -1,6 +1,5 @@ #!/bin/bash - -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. +# Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Reads the current state of a server. The script checks a WebLogic Server state diff --git a/operator/src/main/resources/scripts/startNodeManager.sh b/operator/src/main/resources/scripts/startNodeManager.sh index 4c169255a9a..db383fdbf2e 100644 --- a/operator/src/main/resources/scripts/startNodeManager.sh +++ b/operator/src/main/resources/scripts/startNodeManager.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2017, 2023, Oracle and/or its affiliates. +# Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # diff --git a/operator/src/main/resources/scripts/startServer.sh b/operator/src/main/resources/scripts/startServer.sh index 2b61f105b61..ccb81478898 100755 --- a/operator/src/main/resources/scripts/startServer.sh +++ b/operator/src/main/resources/scripts/startServer.sh @@ -1,6 +1,5 @@ #!/bin/bash - -# Copyright (c) 2017, 2022, Oracle and/or its affiliates. +# Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # diff --git a/operator/src/main/resources/scripts/stopServer.sh b/operator/src/main/resources/scripts/stopServer.sh index 8cc40b2659b..f6cf4c66424 100755 --- a/operator/src/main/resources/scripts/stopServer.sh +++ b/operator/src/main/resources/scripts/stopServer.sh @@ -1,6 +1,5 @@ #!/bin/bash - -# Copyright (c) 2017, 2022, Oracle and/or its affiliates. +# Copyright (c) 2017, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -46,17 +45,32 @@ export SHUTDOWN_TIMEOUT_ARG=${SHUTDOWN_TIMEOUT:-30} # Calculate the wait timeout to issue "kill -9" before the pod is destroyed. # Allow 3 seconds for the NFS v3 manager to detect the process destruction and release file locks. export SIGKILL_WAIT_TIMEOUT=$(expr $SHUTDOWN_TIMEOUT_ARG - 3) -wait_and_kill_after_timeout(){ + +# Block until the given file appears or the given timeout is reached. +wait_file() { + local file="$1"; shift + local wait_seconds="${1:-10}"; shift + test $wait_seconds -lt 1 && echo 'At least 1 second is required' && return 1 + + until test $((wait_seconds--)) -eq 0 -o -e "$file" ; do sleep 1; done + + test $wait_seconds -ge 0 +} + +wait_and_kill_after_timeout() { trace "Wait for ${SIGKILL_WAIT_TIMEOUT} seconds for ${SERVER_NAME} to shut down." >> ${STOP_OUT_FILE} sleep ${SIGKILL_WAIT_TIMEOUT} - trace "The server ${SERVER_NAME} didn't shut down in ${SIGKILL_WAIT_TIMEOUT} seconds, " \ - "killing the server processes." >> ${STOP_OUT_FILE} - # Adjust PATH if necessary before calling jps - adjustPath - - #Specifically killing the NM first as it can auto-restart a killed WL server. - kill -9 `jps -v | grep " NodeManager " | awk '{ print $1 }'` - kill -9 `jps -v | grep -v Jps | awk '{ print $1 }'` + wait_file "${SHUTDOWN_MARKER_FILE}" ${SIGKILL_WAIT_TIMEOUT} || { + trace "The server ${SERVER_NAME} didn't shut down in ${SIGKILL_WAIT_TIMEOUT} seconds, " \ + "killing the server processes." >> ${STOP_OUT_FILE} + # Adjust PATH if necessary before calling jps + adjustPath + + #Specifically killing the NM first as it can auto-restart a killed WL server. + kill -9 `jps -v | grep " NodeManager " | awk '{ print $1 }'` + kill -9 `jps -v | grep -v Jps | awk '{ print $1 }'` + } + touch ${SHUTDOWN_MARKER_FILE} } @@ -106,8 +120,11 @@ check_for_shutdown() { # Check if the server is already shutdown -check_for_shutdown -[ $? -eq 0 ] && trace "Server is already shutting down, is shutdown or failed" &>> ${STOP_OUT_FILE} && exit 0 +if check_for_shutdown ; then + trace "Server is already shutting down, is shutdown or failed" &>> ${STOP_OUT_FILE} + touch ${SHUTDOWN_MARKER_FILE} + exit 0 +fi # Otherwise, connect to the node manager and stop the server instance [ ! -f "${SCRIPTPATH}/wlst.sh" ] && trace SEVERE "Missing file '${SCRIPTPATH}/wlst.sh'." && exit 1 diff --git a/operator/src/main/resources/scripts/tailLog.sh b/operator/src/main/resources/scripts/tailLog.sh index 3f3d1cbbd6c..252d5dae5fc 100755 --- a/operator/src/main/resources/scripts/tailLog.sh +++ b/operator/src/main/resources/scripts/tailLog.sh @@ -1,6 +1,5 @@ #!/bin/bash - -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. +# Copyright (c) 2019, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # diff --git a/operator/src/main/resources/scripts/utils_base.sh b/operator/src/main/resources/scripts/utils_base.sh index ceeb059524f..711cb36ed33 100644 --- a/operator/src/main/resources/scripts/utils_base.sh +++ b/operator/src/main/resources/scripts/utils_base.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. +# Copyright (c) 2021, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Important: Functions defined in this file can work with unknown shells, @@ -48,7 +48,6 @@ # Set TRACE_INCLUDE_FILE env var to false to suppress file name and line number. # umask 027 - export AUXILIARY_IMAGE_COMMAND_LOGS_DIR="${AUXILIARY_IMAGE_COMMAND_LOGS_DIR:-compatibilityModeInitContainerLogs}" trace() { diff --git a/operator/src/main/resources/scripts/wdt_common.sh b/operator/src/main/resources/scripts/wdt_common.sh index 44b317f8231..91a91503e2f 100644 --- a/operator/src/main/resources/scripts/wdt_common.sh +++ b/operator/src/main/resources/scripts/wdt_common.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # limit the file extensions in the model directories From 859db503086818bbb6746ac4a5a7005e00519b98 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 12 Jul 2024 19:06:52 -0400 Subject: [PATCH 106/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 0ae3a4d02ed..e2f2a847a14 100644 --- a/pom.xml +++ b/pom.xml @@ -663,11 +663,11 @@ 21 [3.8.1,) 3.5.0 - 3.3.2 + 3.4.0 3.13.0 3.1.2 3.1.2 - 3.4.1 + 3.4.2 3.3.1 3.12.1 3.3.1 @@ -688,7 +688,7 @@ 2.0.1 1.0.39 1.8.0 - 1.4.0 + 1.5.0 1.4.0 1.17.0 1.7.3 @@ -697,7 +697,7 @@ 0.9.6 3.6.0 1.0.0 - 3.26.0 + 3.26.3 2.16.1 4.2.1 19.0.1 From 3624a0071f6aa6f7ed0a2a237e6ed7b2770a4ecc Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 15 Jul 2024 11:02:36 -0400 Subject: [PATCH 107/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e2f2a847a14..43b1a1c9397 100644 --- a/pom.xml +++ b/pom.xml @@ -672,7 +672,7 @@ 3.12.1 3.3.1 3.4.0 - 3.1.0 + 3.1.1 3.7.0 3.3.1 3.7.1 @@ -720,7 +720,7 @@ 2.17.2 2.2 2.11.0 - 9.2.0 + 10.0.2 2.0.13 1.5.6 ${project.basedir}/src-generated-swagger From 49f0af69652b4aa061ec6574f49d0c7ee4a6d0d8 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 15 Jul 2024 16:45:47 +0000 Subject: [PATCH 108/356] Increased timeout value for introspectorJobActiveDeadlineSeconds, moved some tests from OKE parallel to sequential --- .../oracle/weblogic/kubernetes/ItCoherenceTests.java | 2 +- .../weblogic/kubernetes/ItCrossDomainTransaction.java | 2 +- .../kubernetes/ItDiagnosticsFailedCondition.java | 4 ++-- .../weblogic/kubernetes/ItElasticLoggingFluentd.java | 2 +- .../weblogic/kubernetes/ItExternalLbTunneling.java | 2 +- .../oracle/weblogic/kubernetes/ItInitContainers.java | 1 + .../weblogic/kubernetes/ItIntrospectVersion.java | 2 +- .../weblogic/kubernetes/ItIstioCoherenceTests.java | 2 +- .../kubernetes/ItIstioCrossDomainTransaction.java | 2 +- .../oracle/weblogic/kubernetes/ItIstioDBOperator.java | 2 +- .../weblogic/kubernetes/ItIstioDomainInImage.java | 2 +- .../weblogic/kubernetes/ItIstioManagedCoherence.java | 4 ++-- .../oracle/weblogic/kubernetes/ItIstioMiiDomain.java | 2 +- .../kubernetes/ItIstioProductionSecureMode.java | 2 +- .../weblogic/kubernetes/ItIstioTwoDomainsInImage.java | 2 +- .../weblogic/kubernetes/ItKubernetesDomainEvents.java | 2 +- .../weblogic/kubernetes/ItLBTwoDomainsNginx.java | 11 +++++++++-- .../weblogic/kubernetes/ItLogHomeFlatStructure.java | 2 +- .../oracle/weblogic/kubernetes/ItManageNameSpace.java | 2 +- .../weblogic/kubernetes/ItManagedCoherence.java | 2 +- .../weblogic/kubernetes/ItMaxConcurOptions.java | 2 +- .../weblogic/kubernetes/ItMiiAuxiliaryImage.java | 2 +- .../weblogic/kubernetes/ItMiiClusterResource.java | 2 +- .../java/oracle/weblogic/kubernetes/ItMiiDomain.java | 6 +++--- .../oracle/weblogic/kubernetes/ItMiiMultiModel.java | 2 +- .../weblogic/kubernetes/ItMiiUpdateDomainConfig.java | 4 ++-- .../weblogic/kubernetes/ItMultiDomainModelsScale.java | 2 +- .../ItMultiDomainModelsUpgradeAndScale.java | 2 +- .../kubernetes/ItOpenshiftIstioMiiDomain.java | 2 +- .../weblogic/kubernetes/ItOperatorWlsUpgrade.java | 2 +- .../oracle/weblogic/kubernetes/ItPodTemplates.java | 2 +- .../oracle/weblogic/kubernetes/ItPodsRestart.java | 2 +- .../weblogic/kubernetes/ItPodsShutdownOption.java | 4 ++-- .../weblogic/kubernetes/ItProductionSecureMode.java | 2 +- .../weblogic/kubernetes/ItSessionMigration.java | 2 +- .../oracle/weblogic/kubernetes/ItStickySession.java | 2 +- .../kubernetes/ItUsabilityOperatorHelmChart.java | 2 +- .../kubernetes/ItValidateWebhookReplicas.java | 2 +- .../weblogic/kubernetes/utils/CommonMiiTestUtils.java | 10 +++++----- .../oracle/weblogic/kubernetes/utils/DomainUtils.java | 6 +++--- .../oracle/weblogic/kubernetes/utils/FmwUtils.java | 4 ++-- .../oracle/weblogic/kubernetes/utils/IstioUtils.java | 2 +- .../weblogic/kubernetes/utils/MonitoringUtils.java | 2 +- .../kubernetes/utils/ServerStartPolicyUtils.java | 2 +- 44 files changed, 65 insertions(+), 57 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java index 6492d4a70b4..da2d28bc820 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCoherenceTests.java @@ -357,7 +357,7 @@ private static void createDomainCrAndVerify(String adminSecretName, String domIm .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index 0eebd72a735..b9106d276f0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -629,7 +629,7 @@ private static void createDomainResource(String domainUid, String domNamespace, .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java index 8856d1111f8..0497c2f9b60 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java @@ -897,7 +897,7 @@ private DomainResource createDomainResourceWithConfigMap(String domainUid, .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(introspectorDeadline != null ? introspectorDeadline : 300L))); + .introspectorJobActiveDeadlineSeconds(introspectorDeadline != null ? introspectorDeadline : 3000L))); setPodAntiAffinity(domain); @@ -944,7 +944,7 @@ private DomainResource createDomainResource(String domainUid, String domNamespac .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); ClusterResource cluster = createClusterResource(clusterResName, wlClusterName, domNamespace, replicaCount); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java index 11230c0a705..51969d6f8b9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingFluentd.java @@ -442,7 +442,7 @@ private static void createDomainCrAndVerify(String adminSecretName, .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // create cluster resource if (!Cluster.doesClusterExist(clusterName, CLUSTER_VERSION, domainNamespace)) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java index a589f05d70e..26120f14d09 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java @@ -716,7 +716,7 @@ private static void createDomainResource( .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java index 94cc4dcb50e..264fa650de7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItInitContainers.java @@ -354,6 +354,7 @@ private void createAndVerifyMiiDomain(String domainNamespace, String domainUid, .channelName("default") .nodePort(0)))) .configuration(new Configuration() + .introspectorJobActiveDeadlineSeconds(3000L) .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)))); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index 10499a021c1..ee11ba4c61f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -1482,7 +1482,7 @@ private DomainResource createDomainResourceWithConfigMap(String domainUid, Strin .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(introspectorDeadline != null ? introspectorDeadline : 300L))); + .introspectorJobActiveDeadlineSeconds(introspectorDeadline != null ? introspectorDeadline : 3000L))); setPodAntiAffinity(domain); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java index 6c78264900e..b65f4d10090 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCoherenceTests.java @@ -343,7 +343,7 @@ private static void createDomainCrAndVerify(String adminSecretName, String domIm .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java index 895afe83ff0..4db9d327090 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java @@ -630,7 +630,7 @@ private static void createDomainResource(String domainUid, String domNamespace, .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java index b6dff360fc4..64f172f0a94 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java @@ -605,7 +605,7 @@ private static DomainResource createDomainResourceWithLogHome( .runtimeEncryptionSecret(encryptionSecretName) .onlineUpdate(new OnlineUpdate() .enabled(onlineUpdateEnabled))) - .introspectorJobActiveDeadlineSeconds(300L)); + .introspectorJobActiveDeadlineSeconds(3000L)); if (setDataHome) { domainSpec.dataHome("/shared/data"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java index ab37a499d64..a62f6618f77 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java @@ -335,7 +335,7 @@ private void createDomainResource(String domainUid, String domNamespace, String .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioManagedCoherence.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioManagedCoherence.java index e89f7dd4eb9..dffcbf02de3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioManagedCoherence.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioManagedCoherence.java @@ -75,7 +75,7 @@ @DisplayName("Test to associate a Coherence Cluster with multiple WebLogic server clusters") @IntegrationTest @Tag("kind-parallel") -@Tag("oke-parallelnew") +@Tag("oke-parallel") class ItIstioManagedCoherence { // constants for Coherence @@ -475,7 +475,7 @@ private static void createDomainCrAndVerify(String adminSecretName, String domIm .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // create cluster resource in mii domain for (int i = 1; i <= NUMBER_OF_CLUSTERS; i++) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java index 78b55807d55..6f8673b947f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java @@ -327,7 +327,7 @@ private DomainResource createDomainResource(String domainUid, String domNamespac .onlineUpdate(new OnlineUpdate() .enabled(true)) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return createClusterResourceAndAddReferenceToDomain( domainUid + "-" + clusterName, clusterName, domainNamespace, domain, replicaCount); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java index 27ce7963d89..9e6364165ae 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java @@ -249,7 +249,7 @@ private DomainResource createDomainResource(String domainUid, String domNamespac .configMap(configmapName) .onlineUpdate(new OnlineUpdate().enabled(true)) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return domain; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java index 55695f56065..eef5918f85c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java @@ -392,7 +392,7 @@ private void createDomainResource(String domainUid, String domNamespace, String .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index a76bd8252d3..34e619fc9bb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -150,7 +150,7 @@ @Tag("oke-arm") @IntegrationTest @Tag("olcne-srg") -@Tag("oke-parallel") +@Tag("oke-gate") class ItKubernetesDomainEvents { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index 36e4c59aab6..afc865d35df 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -40,6 +40,7 @@ import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; @@ -519,8 +520,14 @@ private static NginxParams installNginxLB() { nodePortValue = "NodePort"; } - NginxParams params = installAndVerifyNginx(nginxNamespace, IT_LBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT, - IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, nodePortValue); + NginxParams params = null; + if (OKE_CLUSTER_PRIVATEIP) { + params = installAndVerifyNginx(nginxNamespace, 0, + 0, NGINX_CHART_VERSION, nodePortValue); + } else { + params = installAndVerifyNginx(nginxNamespace, IT_LBTWODOMAINSNGINX_INGRESS_HTTP_NODEPORT, + IT_LBTWODOMAINSNGINX_INGRESS_HTTPS_NODEPORT, NGINX_CHART_VERSION, nodePortValue); + } return params; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java index 88f36dfc871..673826a688b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLogHomeFlatStructure.java @@ -337,7 +337,7 @@ private static void createDomainResource( .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L)) + .introspectorJobActiveDeadlineSeconds(3000L)) .replicas(replicaCount)); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java index a5a442f65f7..67358f35031 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java @@ -520,7 +520,7 @@ private DomainResource createDomainResource(String domainNamespace, String domai .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(600L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); domain = createClusterResourceAndAddReferenceToDomain(domainUid + "-" + clusterName, clusterName, domainNamespace, domain, replicaCount); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java index 6a19a74d617..1115bb95ad3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java @@ -341,7 +341,7 @@ private static void createDomainCrAndVerify(String adminSecretName, String domIm .configuration(new Configuration() .model(new Model() .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java index c83114b8247..d9ef37ef99a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java @@ -460,7 +460,7 @@ private static DomainResource createDomainResource(String domainUid, .configMap(configmapName) .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index cac5bbceac4..a0056a7051a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -121,7 +121,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-parallel") +@Tag("oke-gate") class ItMiiAuxiliaryImage { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java index cac12b7e6aa..f3ee4ef6b26 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java @@ -1083,7 +1083,7 @@ private DomainResource createDomainResource(String domainUid, .configMap(configmapName) .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return domain; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java index c1dea3d6cc0..88e218ad06d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java @@ -137,7 +137,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-srg") -@Tag("oke-parallel") +@Tag("oke-gate") class ItMiiDomain { private static String opNamespace = null; @@ -931,7 +931,7 @@ private DomainResource createDomainResource(String domainUid, String domNamespac .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return domain; } @@ -987,7 +987,7 @@ private DomainResource createDomainResourceWithConfigMap(String domainUid, .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return domain; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java index 9f39ad26492..9618f791457 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiMultiModel.java @@ -453,7 +453,7 @@ private DomainResource createDomainResource( .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); if (configMapName != null) { domain.spec().configuration().model().configMap(configMapName); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index 02ccb1a7d30..ca6affcebc9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -119,7 +119,7 @@ @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-srg") -@Tag("oke-parallel") +@Tag("oke-gate") class ItMiiUpdateDomainConfig { private static String opNamespace = null; @@ -1015,7 +1015,7 @@ private static DomainResource createDomainResource( .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L)) + .introspectorJobActiveDeadlineSeconds(3000L)) .replicas(replicaCount)); setPodAntiAffinity(domain); return domain; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java index 73f0b16957b..48fd984f238 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java @@ -536,7 +536,7 @@ private static DomainResource createMiiDomainWithMultiClusters(String domainName .channelName("default") .nodePort(getNextFreePort())))) .configuration(new Configuration() - .introspectorJobActiveDeadlineSeconds(300L) + .introspectorJobActiveDeadlineSeconds(3000L) .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)))); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsUpgradeAndScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsUpgradeAndScale.java index 071291e114c..d1a4752182f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsUpgradeAndScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsUpgradeAndScale.java @@ -290,7 +290,7 @@ private static DomainResource createMiiDomainWithMultiClusters(String domainName .channelName("default") .nodePort(getNextFreePort())))) .configuration(new Configuration() - .introspectorJobActiveDeadlineSeconds(300L) + .introspectorJobActiveDeadlineSeconds(3000L) .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)))); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java index 32f5672bbae..61abe80772e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java @@ -307,7 +307,7 @@ private DomainResource createDomainResource(String domainUid, String domNamespac .configMap(configmapName) .onlineUpdate(new OnlineUpdate().enabled(true)) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return domain; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java index 3dc8c512e03..e057a3c18ab 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java @@ -645,7 +645,7 @@ private void createDomainResource( .model(new Model() .runtimeEncryptionSecret(encryptionSecretName) .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); boolean domCreated = assertDoesNotThrow(() -> createDomainCustomResource(domain, domVersion), String.format("Create domain custom resource failed with ApiException for %s in namespace %s", domainUid, domainNamespace)); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java index eb356a32931..d7db1e37f85 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java @@ -291,7 +291,7 @@ private static void createDomainCrAndVerify(String adminSecretName, .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); ClusterSpec clusterSpec = new ClusterSpec() diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java index 5be97ce5adb..8b7ff0f95e4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsRestart.java @@ -686,7 +686,7 @@ private static void createAndVerifyMiiDomain() { .serverStartPolicy("IfNeeded") .serverPod(srvrPod) .configuration(new Configuration() - .introspectorJobActiveDeadlineSeconds(300L) + .introspectorJobActiveDeadlineSeconds(3000L) .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)))); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java index 33f4dbca322..733ab1025c8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java @@ -82,7 +82,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-parallel") +@Tag("oke-gate") class ItPodsShutdownOption { private static String domainNamespace = null; @@ -480,7 +480,7 @@ private DomainResource buildDomainResource(Shutdown[] shutDownObject) { .configMap(cmName) .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(600L)) + .introspectorJobActiveDeadlineSeconds(3000L)) .addManagedServersItem(new ManagedServer() .serverStartPolicy("Always") .serverName(indManagedServerName1) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java index 9a36a1b8db9..dabdb308976 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java @@ -429,7 +429,7 @@ private static void createDomainResource( .runtimeEncryptionSecret(encryptionSecretName) .onlineUpdate(new OnlineUpdate() .enabled(true))) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); logger.info("Create domain custom resource for domainUid {0} in namespace {1}", domainUid, domNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java index bf834c63aec..83611e4427b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java @@ -382,7 +382,7 @@ private static void createDomainCrAndVerify(String adminSecretName, .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // create cluster resource domain = createClusterResourceAndAddReferenceToDomain( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java index e7609fa3957..8ae4a1e3f01 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java @@ -376,7 +376,7 @@ private static void createDomainCrAndVerify(String adminSecretName, .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java index cc04c5204ea..16c650475b8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItUsabilityOperatorHelmChart.java @@ -982,7 +982,7 @@ private void createAndVerifyMiiDomain(String domainNamespace, String domainUid) .channelName("default") .nodePort(0)))) .configuration(new Configuration() - .introspectorJobActiveDeadlineSeconds(280L) + .introspectorJobActiveDeadlineSeconds(3000L) .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)))); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java index b56cf774899..1d8a4dd1b17 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItValidateWebhookReplicas.java @@ -626,7 +626,7 @@ private static DomainResource createMiiDomainWithMultiClusters(String domainName .channelName("default") .nodePort(getNextFreePort())))) .configuration(new Configuration() - .introspectorJobActiveDeadlineSeconds(300L) + .introspectorJobActiveDeadlineSeconds(3000L) .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionSecretName)))); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index 0455d203c53..2c25680a230 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -512,7 +512,7 @@ public static DomainResource createDomainResource( .model(new oracle.weblogic.domain.Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(600L))); + .introspectorJobActiveDeadlineSeconds(3000L))); domain.spec().setImagePullSecrets(secrets); @@ -711,7 +711,7 @@ public static DomainResource createDomainResourceWithAuxiliaryImage( .model(new oracle.weblogic.domain.Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(600L))); + .introspectorJobActiveDeadlineSeconds(3000L))); domain.spec().setImagePullSecrets(secrets); setPodAntiAffinity(domain); return domain; @@ -843,7 +843,7 @@ public static DomainResource createDomainResourceWithLogHome( .runtimeEncryptionSecret(encryptionSecretName) .onlineUpdate(new OnlineUpdate() .enabled(onlineUpdateEnabled))) - .introspectorJobActiveDeadlineSeconds(600L)); + .introspectorJobActiveDeadlineSeconds(3000L)); if (setDataHome) { domainSpec.dataHome(uniquePath + "/data"); @@ -1993,7 +1993,7 @@ public static DomainResource createMiiDomainWithIstioMultiClusters(String domain .model(new Model() .domainType(WLS_DOMAIN_TYPE) .runtimeEncryptionSecret(encryptionsecret)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); @@ -2114,7 +2114,7 @@ public static DomainResource create2channelsDomainResourceWithConfigMap(String d .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); return domain; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java index e8636c2d127..a8de8825dde 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java @@ -1502,7 +1502,7 @@ public static DomainResource createDomainResourceForDomainInImage(String domainU .configuration(new Configuration() .model(new Model() .domainType(WLS_DOMAIN_TYPE)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // create cluster resource for the domain if (!Cluster.doesClusterExist(clusterResName, CLUSTER_VERSION, domainNamespace)) { @@ -1620,7 +1620,7 @@ public static DomainResource createMiiDomainResourceWithConfigMap(String domain .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(introspectorDeadline != null ? introspectorDeadline : 300L))); + .introspectorJobActiveDeadlineSeconds(introspectorDeadline != null ? introspectorDeadline : 3000L))); // create cluster resource for the domain if (!Cluster.doesClusterExist(clusterName, CLUSTER_VERSION, domainNamespace)) { @@ -1700,7 +1700,7 @@ public static DomainResource createDomainResourceForDomainInImageWithConfigMap(S .model(new Model() .domainType(WLS_DOMAIN_TYPE) .configMap(configmapName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // create cluster resource for the domain if (!doesClusterExist(clusterName, CLUSTER_VERSION, domainNamespace)) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java index e18b6b35e70..52c8f72806d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FmwUtils.java @@ -140,7 +140,7 @@ public static DomainResource createDomainResource( .domainType("JRF") .runtimeEncryptionSecret(encryptionSecretName)) .addSecretsItem(rcuAccessSecretName) - .introspectorJobActiveDeadlineSeconds(900L))); + .introspectorJobActiveDeadlineSeconds(3000L))); return domain; } @@ -227,7 +227,7 @@ public static DomainResource createIstioDomainResource( .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) .addSecretsItem(rcuAccessSecretName) - .introspectorJobActiveDeadlineSeconds(600L))); + .introspectorJobActiveDeadlineSeconds(3000L))); } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index 9b0c167b8b0..136802028a5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -433,7 +433,7 @@ public static DomainResource createIstioDomainResource(String domainUid, String .configMap(configmapName) .onlineUpdate(new OnlineUpdate().enabled(true)) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // create cluster resource domain = createClusterResourceAndAddReferenceToDomain(domainUid + "-" + clusterName, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index aa58be3bdb8..b2fa561bcd8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -1039,7 +1039,7 @@ public static void createDomainCrAndVerify(String adminSecretName, .model(new Model() .domainType("WLS") .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(300L))); + .introspectorJobActiveDeadlineSeconds(3000L))); // add clusters to the domain resource ClusterList clusters = Cluster.listClusterCustomResources(namespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index 135bc3a500a..b9d196e4b3e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -269,7 +269,7 @@ public static void createDomainResource( .domainType("WLS") .configMap(configmapName) .runtimeEncryptionSecret(encryptionSecretName)) - .introspectorJobActiveDeadlineSeconds(600L))); + .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); createClusterResourceAndAddReferenceToDomain( From a67a6a5b46cfc31d3f7b49ce4a96a07a7e21beed Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Thu, 18 Jul 2024 15:14:38 +0000 Subject: [PATCH 109/356] Change to call real wlst.sh for model-diff.py, calling jython directly has... --- .../src/main/resources/scripts/model-diff.py | 15 +++++----- .../main/resources/scripts/modelInImage.sh | 28 +++++++++++-------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/operator/src/main/resources/scripts/model-diff.py b/operator/src/main/resources/scripts/model-diff.py index 0cdd3efa322..b58e84aa799 100644 --- a/operator/src/main/resources/scripts/model-diff.py +++ b/operator/src/main/resources/scripts/model-diff.py @@ -154,17 +154,16 @@ def main(): rcfh = open('/tmp/model_diff_rc', 'w') rcfh.write(",".join(map(str,changed_items))) rcfh.close() - System.exit(0) + exit() except: exc_type, exc_obj, exc_tb = sys.exc_info() eeString = traceback.format_exception(exc_type, exc_obj, exc_tb) print eeString - System.exit(-1) -if __name__ == "__main__": - all_changes = [] - all_added = [] - all_removed = [] - changed_items = [] - main() + sys.exit(2) +all_changes = [] +all_added = [] +all_removed = [] +changed_items = [] +main() diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 0c97236cf82..f0d3e7a97cb 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -631,7 +631,8 @@ diff_model() { local JAVA_PROPS="-Dpython.console= ${JAVA_PROPS} -Djava.security.egd=file:/dev/./urandom" local CP=${ORACLE_SERVER_DIR}/server/lib/weblogic.jar # Get partial models for sanity check for forbidden attribute change - local SERVER_OR_SERVERTEMPLATES_NAMES=$(jq '{ topology: { Server: (.topology.Server | with_entries(.value = {})), + local SERVER_OR_SERVERTEMPLATES_NAMES + SERVER_OR_SERVERTEMPLATES_NAMES=$(jq '{ topology: { Server: (.topology.Server | with_entries(.value = {})), ServerTemplate: (if .topology.ServerTemplate then (.topology.ServerTemplate | with_entries(.value = {})) else empty end) }} | if .topology.ServerTemplate == {} then del(.topology.ServerTemplate) else . end' $2) rc=$? @@ -639,19 +640,22 @@ diff_model() { trace SEVERE "Failed to extract server names from original model using jq "$rc exitOrLoop fi - local PARTIAL_DIFFED_MODEL=$(jq '{domainInfo: .domainInfo, topology: .topology} | with_entries(select(.value != null))' /tmp/diffed_model.json) - rc=$? - if [ $rc -ne 0 ] ; then - trace SEVERE "Failed to extract domainInfo and topology from delta model using jq "$rc - exitOrLoop + local PARTIAL_DIFFED_MODEL + if [ -f /tmp/diffed_model.json ] ; then + PARTIAL_DIFFED_MODEL=$(jq '{domainInfo: .domainInfo, topology: .topology} | with_entries(select(.value != null))' /tmp/diffed_model.json) + rc=$? + if [ $rc -ne 0 ] ; then + trace SEVERE "Failed to extract domainInfo and topology from delta model using jq "$rc + exitOrLoop + fi + else + PARTIAL_DIFFED_MODEL="{}" fi - - ${JAVA_HOME}/bin/java -cp ${CP} \ - ${JAVA_PROPS} \ - org.python.util.jython \ - ${SCRIPTPATH}/model-diff.py "$SERVER_OR_SERVERTEMPLATES_NAMES" "$PARTIAL_DIFFED_MODEL" > ${WDT_OUTPUT} 2>&1 + # Use the real wlst.sh and not the operator wrap script, calling jypthon directory has problem understanding WLST boolean false, true etc.. + # eval will fail + ${ORACLE_HOME}/oracle_common/common/bin/wlst.sh ${SCRIPTPATH}/model-diff.py "${SERVER_OR_SERVERTEMPLATES_NAMES}" "${PARTIAL_DIFFED_MODEL}" > ${WDT_OUTPUT} 2>&1 if [ $? -ne 0 ] ; then - trace SEVERE "Failed to compare models. Error output:" + trace SEVERE "Failed to interpret models results . Error output:" cat ${WDT_OUTPUT} exitOrLoop fi From cab80451c2ace859173e10f89c17a1e379b9aed4 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 18 Jul 2024 11:26:54 -0400 Subject: [PATCH 110/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 43b1a1c9397..206fa2da1fc 100644 --- a/pom.xml +++ b/pom.xml @@ -690,7 +690,7 @@ 1.8.0 1.5.0 1.4.0 - 1.17.0 + 1.17.1 1.7.3 0.1.0 2.9.0 @@ -720,7 +720,7 @@ 2.17.2 2.2 2.11.0 - 10.0.2 + 10.0.3 2.0.13 1.5.6 ${project.basedir}/src-generated-swagger From 31a63f34c7fbfd4c470f636fae514ed3f12d950a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 22 Jul 2024 13:29:13 -0400 Subject: [PATCH 111/356] Prepare for WKO 4.2.4 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 4e2c65d7a4c..a311bffb28a 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 841160073e2..8ca59cd3986 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index ccc7bef7dc4..a4f60eb2f43 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 8e8d4fb01b9..1ca1f277034 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.4-SNAPSHOT + 4.2.4 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 55578188e2f..bb15bf2ad3f 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index cb5503d4f4b..dd7f00bf0e5 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.4-SNAPSHOT + 4.2.4 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 843a20afe4e..070bb4f4952 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 206fa2da1fc..46e9bada249 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 1cce70deaef..6af7e7b6cbe 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4-SNAPSHOT + 4.2.4 operator-swagger From 8ae0d394b9f53868a73c8d1ec453b96e946c81e0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 22 Jul 2024 14:05:55 -0400 Subject: [PATCH 112/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index a311bffb28a..d98f7725e5d 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 8ca59cd3986..0668d879d14 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index a4f60eb2f43..8dc77dfa0fb 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 1ca1f277034..ea10817dd2e 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.4 + 4.2.5-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index bb15bf2ad3f..e91ee438e75 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index dd7f00bf0e5..186504a2aab 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.4 + 4.2.5-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 070bb4f4952..3db2c9370cf 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 46e9bada249..15421665757 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 6af7e7b6cbe..8553dd501f1 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.4 + 4.2.5-SNAPSHOT operator-swagger From b2579ca6ab3431ac98ab8cb65f56d903263955ed Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 23 Jul 2024 16:28:47 +0000 Subject: [PATCH 113/356] Restart core-dns if DNS resolution fails --- .../kubernetes/ItCrossDomainTransaction.java | 19 +++++++++++++++++++ .../kubernetes/utils/ApplicationUtils.java | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index b9106d276f0..5481f27ff63 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -43,6 +43,7 @@ import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; import oracle.weblogic.kubernetes.utils.OracleHttpClient; import org.junit.jupiter.api.BeforeAll; @@ -58,6 +59,7 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; @@ -93,6 +95,7 @@ import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; @@ -688,6 +691,22 @@ private static void createNginxIngressPathRoutingRules() { String curlCmd = "curl -g --silent --show-error --noproxy '*' http://" + hostAndPort + "/weblogic/ready --write-out %{http_code} -o /dev/null"; + if (OKE_CLUSTER) { + try { + if (!callWebAppAndWaitTillReady(curlCmd, 60)) { + ExecResult result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); + logger.info(result.stdout()); + //restart core-dns service + result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); + logger.info(result.stdout()); + checkPodReady("core-dns", null, "kube-system"); + result = ExecCommand.exec(curlCmd); + logger.info(result.stdout()); + } + } catch (Exception ex) { + logger.warning(ex.getLocalizedMessage()); + } + } logger.info("Executing curl command {0}", curlCmd); assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java index 300f365f113..fe61f98bf69 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java @@ -382,7 +382,7 @@ public static boolean callWebAppAndWaitTillReturnedCode(String curlCmd, String h httpStatusCode, responseCode, i, maxIterations); try { - Thread.sleep(1000); + Thread.sleep(5000); } catch (InterruptedException ignore) { // ignore } From 5662fd24cddce73daa4a1a1fba5c55780c5a157f Mon Sep 17 00:00:00 2001 From: maggie_he Date: Tue, 23 Jul 2024 19:21:05 +0000 Subject: [PATCH 114/356] Fix for integ tests on OKD --- .../oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java | 6 ++++-- .../weblogic/kubernetes/ItMiiDynamicUpdatePart3.java | 9 +-------- .../weblogic/kubernetes/assertions/impl/Application.java | 1 + 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index a0056a7051a..f0693001067 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -79,7 +80,6 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyConfiguredSystemResouceByPath; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyConfiguredSystemResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapForDomainCreation; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeExists; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeHasExpectedStatus; @@ -304,6 +304,7 @@ WEBLOGIC_IMAGE_TO_USE_IN_SPEC, adminSecretName, createSecretsForImageRepos(domai * Verify domain is rolling restarted. */ @Test + @Order(1) @DisplayName("Test to update data source url in the domain using auxiliary image") @Tag("gate") void testUpdateDataSourceInDomainUsingAuxiliaryImage() { @@ -390,6 +391,7 @@ void testUpdateDataSourceInDomainUsingAuxiliaryImage() { * Verify configured JMS and JDBC resources. */ @Test + @Order(2) @DisplayName("Test to update Base Weblogic Image Name") void testUpdateBaseImageName() { // get the original domain resource before update @@ -1559,7 +1561,7 @@ private void checkApplicationIsAccessible( int replicaCount, String expectedResponse) { for (int i = 1; i <= replicaCount; i++) { int index = i; - testUntil(withStandardRetryPolicy, + testUntil(withLongRetryPolicy, () -> appAccessibleInPod(domainNamespace, managedServerPrefixDomain1 + index, "8001", "sample-war/index.jsp", expectedResponse + index), logger, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java index 467c11778e9..9f1d054d161 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java @@ -54,7 +54,6 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkSystemResourceConfigViaAdminPod; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifySystemResourceConfiguration; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; import static oracle.weblogic.kubernetes.utils.K8sEvents.DOMAIN_FAILED; @@ -412,14 +411,8 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() { int adminServiceNodePort = getServiceNodePort(helper.domainNamespace, getExternalServicePodName(helper.adminServerPodName), "default"); assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid"); - if (TestConstants.KIND_CLUSTER - && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - assertFalse(checkSystemResourceConfig(helper.adminSvcExtHost, adminServiceNodePort, "JDBCSystemResources", + assertFalse(checkSystemResourceConfig(helper.adminSvcExtHost, adminServiceNodePort, "JDBCSystemResources", "TestDataSource2"), "Found JDBCSystemResource datasource, should be deleted"); - } else { - verifySystemResourceConfiguration(null, adminServiceNodePort, - "JDBCSystemResources", "TestDataSource2", "404", httpHostHeader); - } } logger.info("JDBCSystemResource Datasource is deleted"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Application.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Application.java index 73a09476aa8..2dd00c5ba2d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Application.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Application.java @@ -109,6 +109,7 @@ public static boolean appAccessibleInPodKubernetesCLI( podName, port, appPath); + getLogger().info("The command to access app is: {0}", cmd); CommandParams params = Command .defaultCommandParams() From 1de7a2b40997349aa8b1f30e6cb4640fa2251f26 Mon Sep 17 00:00:00 2001 From: jshum Date: Wed, 24 Jul 2024 17:36:37 -0500 Subject: [PATCH 115/356] fix jq error for model without server template --- operator/src/main/resources/scripts/modelInImage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index f0d3e7a97cb..74aaff70b64 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -633,7 +633,7 @@ diff_model() { # Get partial models for sanity check for forbidden attribute change local SERVER_OR_SERVERTEMPLATES_NAMES SERVER_OR_SERVERTEMPLATES_NAMES=$(jq '{ topology: { Server: (.topology.Server | with_entries(.value = {})), - ServerTemplate: (if .topology.ServerTemplate then (.topology.ServerTemplate | with_entries(.value = {})) else empty end) + ServerTemplate: (if .topology.ServerTemplate then (.topology.ServerTemplate | with_entries(.value = {})) else {} end) }} | if .topology.ServerTemplate == {} then del(.topology.ServerTemplate) else . end' $2) rc=$? if [ $rc -ne 0 ] ; then From 9f6e170ad6a6d028c24060f936a6f7903f0c577e Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Fri, 26 Jul 2024 18:21:07 +0000 Subject: [PATCH 116/356] update Create OKE script to add retry in case of connection to Kubernetes server error --- Jenkinsfile.oke | 7 ++-- .../oke/terraform/okemodule/oke.create.sh | 39 ++++++++++++++----- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index b00390a1a21..b59891b0666 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -425,7 +425,7 @@ EOF mkdir -p ${WORKSPACE}/terraform/terraforminstall sh ${WORKSPACE}/terraform/oke.create.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == "'"${CLUSTER_NAME}"'" and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} @@ -468,8 +468,7 @@ EOF echo "getting MountTarget ID" mount_target_id=${MOUNT_TARGET_OCID} - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') - + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == "'"${CLUSTER_NAME}"'" and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" @@ -530,7 +529,7 @@ EOF touch ${WORKSPACE}/.mvn/maven.config export KUBECONFIG=${kubeconfig_file} - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == "'"${CLUSTER_NAME}"'" and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh index 9648d5f74f5..dffa9220f3c 100755 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh @@ -84,13 +84,32 @@ createRoleBindings () { checkClusterRunning () { echo "Confirm we have ${KUBERNETES_CLI:-kubectl} working..." - myline=`${KUBERNETES_CLI:-kubectl} get nodes | awk '{print $2}'| tail -n+2` - status="NotReady" - max=50 - count=1 - - privateIP=${vcn_cidr_prefix//./\\.}\\.10\\. privateIP=${vcn_cidr_prefix} + myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) + if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then + echo "[ERROR] Unable to connect to the server: net/http: TLS handshake timeout" + + echo '- could not talk to OKE cluster, aborting' + cd ${terraformVarDir} + terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + terraform apply -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + echo "retrying to execute KUBERNETES_CLI" + clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + echo "clusterIp : $clusterIP" + clusterPublicIP=${clusterIP:1:-6} + echo " clusterPublicIP : ${clusterPublicIP}" + echo "NO_PROXY before : ${NO_PROXY}" + export NO_PROXY=${clusterPublicIP} + echo "NO_PROXY:" $NO_PROXY + myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) + if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then + echo "[ERROR] Unable to connect to the server: net/http: TLS handshake timeout" + echo '- could not talk to OKE cluster, aborting' + cd ${terraformVarDir} + terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + exit 1 + fi + fi declare -a myline myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` @@ -116,7 +135,7 @@ checkClusterRunning () { if [ "$NODES" == "2" ]; then echo '- looks good' else - echo '- could not talk to cluster, aborting' + echo '- could not talk to OKE cluster, aborting' cd ${terraformVarDir} terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars exit 1 @@ -183,15 +202,15 @@ export KUBECONFIG=${terraformVarDir}/${okeclustername}_kubeconfig export okeclustername=\"${okeclustername}\" - echo " oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"'" +echo " oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"'" clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" -export NO_PROXY=$NO_PROXY,${clusterPublicIP} +echo "NO_PROXY before : ${NO_PROXY}" +export NO_PROXY=${clusterPublicIP} echo "NO_PROXY:" $NO_PROXY - checkClusterRunning echo "$okeclustername is up and running}" From cdf3e57c60bda9bbdfc548e41c683edd2229e08e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 26 Jul 2024 14:58:21 -0400 Subject: [PATCH 117/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 15421665757..27a76f0f3f9 100644 --- a/pom.xml +++ b/pom.xml @@ -673,7 +673,7 @@ 3.3.1 3.4.0 3.1.1 - 3.7.0 + 3.8.0 3.3.1 3.7.1 3.6.0 @@ -688,7 +688,7 @@ 2.0.1 1.0.39 1.8.0 - 1.5.0 + 1.5.1 1.4.0 1.17.1 1.7.3 From 1469541cfe267182b44bf54b3d86157cba63a946 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 26 Jul 2024 15:09:07 -0400 Subject: [PATCH 118/356] Prepare for operator 4.2.5 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index d98f7725e5d..0424f1bfef1 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 0668d879d14..7c00032d0c1 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 8dc77dfa0fb..d2b9aee11be 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index ea10817dd2e..ab29a38faac 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.5-SNAPSHOT + 4.2.5 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index e91ee438e75..8b1ab223de0 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 186504a2aab..c4ddd08c76c 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.5-SNAPSHOT + 4.2.5 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 3db2c9370cf..d531845323f 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 27a76f0f3f9..98e3aa1960f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 8553dd501f1..9d168552f6c 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5-SNAPSHOT + 4.2.5 operator-swagger From 889f73f7ba5489711c8830906f621c162c8eb7d5 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 26 Jul 2024 15:29:32 -0400 Subject: [PATCH 119/356] Prepare for next development version --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 0424f1bfef1..9474816cdd0 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 7c00032d0c1..c945a9bbe3e 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index d2b9aee11be..fbbf18b6bec 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index ab29a38faac..f34cd6d0a15 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.5 + 4.2.6-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 8b1ab223de0..9742838c8bf 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index c4ddd08c76c..01ac2518ddf 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.5 + 4.2.6-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index d531845323f..c646afee3c1 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 98e3aa1960f..1244620ec30 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 9d168552f6c..567988d50ca 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.5 + 4.2.6-SNAPSHOT operator-swagger From 3fa77414b28a13f635734de1dc41dc448414ca5c Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Sat, 27 Jul 2024 02:31:56 +0000 Subject: [PATCH 120/356] Update ItElasticLoggingSample in r42 to run on internal Jenkin --- .../oracle/weblogic/kubernetes/ItElasticLoggingSample.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingSample.java index 5775c2a1f38..637a9eb92c1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItElasticLoggingSample.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -68,6 +68,7 @@ @DisplayName("ELK Stack sample to test to use Elasticsearch API to query Operator logs") @IntegrationTest @Tag("kind-parallel") +@Tag("oke-parallel") class ItElasticLoggingSample { // constants for namespaces private static String domainNamespace = null; From 94e3af2c5116a285d46d8a414b6357e3b6f9e7f2 Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Wed, 31 Jul 2024 17:21:05 +0000 Subject: [PATCH 121/356] [wko-nightly] Fix ItFmwDomainOnPVSample.testInitialMain nightly failures in release/4.2 --- .../kubernetes/ItFmwDomainOnPVSample.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java index efda901232c..54f0722d81f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import oracle.weblogic.kubernetes.actions.impl.UniqueName; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; @@ -55,12 +56,13 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.backupReports; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.restoreReports; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.SampleUtils.createPVHostPathAndChangePermissionInKindCluster; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test and verify Domain on PV FMW domain sample. @@ -256,7 +258,11 @@ public void testInitialMain() { createPVHostPathAndChangePermissionInKindCluster("/shared", envMap); } - execTestScriptAndAssertSuccess("-initial-main", "Failed to run -initial-main"); + testUntil( + withLongRetryPolicy, + checkTestScriptAndAssertSuccess("-initial-main", "Failed to run -initial-main"), + logger, + "create PV HostPath and change Permission in Kind Cluster"); } /** @@ -264,8 +270,8 @@ public void testInitialMain() { * @param arg arguments to execute script * @param errString a string of detailed error */ - private void execTestScriptAndAssertSuccess(String arg, - String errString) { + private boolean execTestScriptAndAssertSuccess(String arg, + String errString) { Assumptions.assumeTrue(previousTestSuccessful); previousTestSuccessful = false; @@ -292,9 +298,15 @@ private void execTestScriptAndAssertSuccess(String arg, outStr += ", stderr=\n{\n" + (result != null ? result.stderr() : "") + "\n}\n"; outStr += ", stdout=\n{\n" + (result != null ? result.stdout() : "") + "\n}\n"; - assertTrue(success, outStr); + logger.info("output String is: {0}", outStr); previousTestSuccessful = true; + + return success; + } + + private Callable checkTestScriptAndAssertSuccess(String arg, String errString) { + return () -> execTestScriptAndAssertSuccess(arg, errString); } /** From c19dc590875613618a02e16090e1e6130965ce82 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 6 Aug 2024 13:23:34 -0400 Subject: [PATCH 122/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 1244620ec30..8eea19e071e 100644 --- a/pom.xml +++ b/pom.xml @@ -702,7 +702,7 @@ 4.2.1 19.0.1 3.0.1u2 - 2.0.0 + 2.0.10 4.12.0 3.9.0 1.78.1 @@ -711,8 +711,8 @@ 1.7.0 1.3.2 UTF-8 - 3.1.7 - 1.1.6 + 3.1.8 + 1.1.7 4.0.2 6.1.0 0.16.0 From 9ff5fa029e7e3479d3bac3c690148a41001e1fe4 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 6 Aug 2024 21:18:35 +0000 Subject: [PATCH 123/356] retry the api call when there is an ApiException --- .../kubernetes/ItIntrospectVersion.java | 5 -- .../assertions/impl/Kubernetes.java | 51 +++++++++++++------ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index ee11ba4c61f..a3035c855ff 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -390,11 +390,6 @@ void testDomainIntrospectVersionNotRolling() { //verify admin server accessibility and the health of cluster members verifyMemberHealth(adminServerPodName, managedServerNames, wlsUserName, wlsPassword); - // verify each managed server can see other member in the cluster - for (String managedServerName : managedServerNames) { - verifyConnectionBetweenClusterMembers(managedServerName, managedServerNames); - } - //update the global replica count since the test changed the replica count of cluster1 to 3 cluster1ReplicaCount = 3; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java index 8c6212999ea..97d2fc4cf9b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/assertions/impl/Kubernetes.java @@ -462,21 +462,42 @@ public static boolean isTraefikPodReady(String namespace) throws ApiException { * @throws ApiException when there is error in querying the cluster */ public static V1Pod getPod(String namespace, String labelSelector, String podName) throws ApiException { - V1PodList v1PodList = - coreV1Api.listNamespacedPod( - namespace, // namespace in which to look for the pods. - Boolean.FALSE.toString(), // // pretty print output. - Boolean.FALSE, // allowWatchBookmarks requests watch events with type "BOOKMARK". - null, // continue to query when there is more results to return. - null, // selector to restrict the list of returned objects by their fields - labelSelector, // selector to restrict the list of returned objects by their labels. - null, // maximum number of responses to return for a list call. - null, // shows changes that occur after that particular version of a resource. - RESOURCE_VERSION_MATCH_UNSET, // String | how to match resource version, leave unset - SEND_INITIAL_EVENTS_UNSET, // Boolean | if to send initial events - null, // Timeout for the list/watch call. - Boolean.FALSE // Watch for changes to the described resources. - ); + V1PodList v1PodList = null; + int maxRetries = 3; + int retryCount = 0; + boolean success = false; + while (retryCount < maxRetries && !success) { + try { + v1PodList + = coreV1Api.listNamespacedPod( + namespace, // namespace in which to look for the pods. + Boolean.FALSE.toString(), // // pretty print output. + Boolean.FALSE, // allowWatchBookmarks requests watch events with type "BOOKMARK". + null, // continue to query when there is more results to return. + null, // selector to restrict the list of returned objects by their fields + labelSelector, // selector to restrict the list of returned objects by their labels. + null, // maximum number of responses to return for a list call. + null, // shows changes that occur after that particular version of a resource. + RESOURCE_VERSION_MATCH_UNSET, // String | how to match resource version, leave unset + SEND_INITIAL_EVENTS_UNSET, // Boolean | if to send initial events + null, // Timeout for the list/watch call. + Boolean.FALSE // Watch for changes to the described resources. + ); + success = true; + } catch (ApiException ex) { + retryCount++; + if (retryCount >= maxRetries) { + System.out.println("Max retries reached. Giving up."); + throw ex; + } else { + try { + Thread.sleep(1000); // Wait for 1 second + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + } + } for (V1Pod item : v1PodList.getItems()) { if (item.getMetadata().getName().contains(podName.trim())) { getLogger().info("Name: {0}, Namespace: {1}, Phase: {2}", From eddd800906be5c1cff283ad34ed6a2443f446621 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 6 Aug 2024 23:21:05 +0000 Subject: [PATCH 124/356] Access secure weblogic server in istio service mesh --- .../ItIstioProductionSecureMode.java | 54 +++++++----- .../weblogic/kubernetes/utils/IstioUtils.java | 12 ++- .../istio-productionsecure-tls-mode.yaml | 82 +++++++++++++++++++ 3 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 integration-tests/src/test/resources/istio/istio-productionsecure-tls-mode.yaml diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java index 9e6364165ae..67c43a7dd95 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java @@ -3,6 +3,8 @@ package oracle.weblogic.kubernetes; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; @@ -31,20 +33,26 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.ISTIO_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.addLabelsToNamespace; import static oracle.weblogic.kubernetes.actions.TestActions.createConfigMap; +import static oracle.weblogic.kubernetes.utils.ApplicationUtils.checkAppUsingHostHeader; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.IstioUtils.createAdminServer; import static oracle.weblogic.kubernetes.utils.IstioUtils.deployHttpIstioGatewayAndVirtualservice; -import static oracle.weblogic.kubernetes.utils.IstioUtils.deployIstioDestinationRule; import static oracle.weblogic.kubernetes.utils.IstioUtils.getIstioHttpIngressPort; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; @@ -71,6 +79,8 @@ class ItIstioProductionSecureMode { private final String adminServerPodName = domainUid + "-admin-server"; private final String managedServerPrefix = domainUid + "-managed-server"; private final int replicaCount = 1; + private static final String istioNamespace = "istio-system"; + private static final String istioIngressServiceName = "istio-ingressgateway"; private static LoggingFacade logger = null; /** @@ -115,7 +125,7 @@ public static void initAll(@Namespaces(2) List namespaces) { */ @Test @DisplayName("Create WebLogic SecureMode Domain with mii model with istio") - void testIstioModelInImageSecureModeDomain() { + void testIstioModelInImageSecureModeDomain() throws UnknownHostException { // Create the repo secret to pull the image // this secret is used only for non-kind cluster @@ -178,34 +188,36 @@ void testIstioModelInImageSecureModeDomain() { checkPodReadyAndServiceExists(managedServerPrefix + i, domainUid, domainNamespace); } - String clusterService = domainUid + "-cluster-" + clusterName + "." + domainNamespace + ".svc.cluster.local"; - - Map templateMap = new HashMap<>(); + Map templateMap = new HashMap<>(); templateMap.put("NAMESPACE", domainNamespace); - templateMap.put("DUID", domainUid); - templateMap.put("ADMIN_SERVICE",adminServerPodName); - templateMap.put("CLUSTER_SERVICE", clusterService); - Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); + Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-productionsecure-tls-mode.yaml"); Path targetHttpFile = assertDoesNotThrow( - () -> generateFileFromTemplate(srcHttpFile.toString(), "istio-http.yaml", templateMap)); + () -> generateFileFromTemplate(srcHttpFile.toString(), "istio-productionsecure-tls-mode.yaml", templateMap)); logger.info("Generated Http VS/Gateway file path is {0}", targetHttpFile); boolean deployRes = assertDoesNotThrow( () -> deployHttpIstioGatewayAndVirtualservice(targetHttpFile)); assertTrue(deployRes, "Failed to deploy Http Istio Gateway/VirtualService"); - - Path srcDrFile = Paths.get(RESOURCE_DIR, "istio", "istio-dr-template.yaml"); - Path targetDrFile = assertDoesNotThrow( - () -> generateFileFromTemplate(srcDrFile.toString(), "istio-dr.yaml", templateMap)); - logger.info("Generated DestinationRule file path is {0}", targetDrFile); - - deployRes = assertDoesNotThrow( - () -> deployIstioDestinationRule(targetDrFile)); - assertTrue(deployRes, "Failed to deploy Istio DestinationRule"); - - int istioIngressPort = getIstioHttpIngressPort(); + + String host = formatIPv6Host(K8S_NODEPORT_HOST); + int istioIngressPort = getIstioHttpIngressPort("https"); logger.info("Istio Ingress Port is {0}", istioIngressPort); + logger.info("host {0}", host); + String hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null + ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) : host + ":" + istioIngressPort; + + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !OCNE) { + istioIngressPort = ISTIO_HTTPS_HOSTPORT; + hostAndPort = InetAddress.getLocalHost().getHostAddress() + ":" + istioIngressPort; + } + String url = "https://" + hostAndPort + "/weblogic/ready"; + testUntil( + () -> checkAppUsingHostHeader(url, + "istio-mii-securemode-admin-server.NAMESPACE.svc.cluster.local".replace("NAMESPACE", domainNamespace)), + logger, + "application to be ready {0}", + url); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index 136802028a5..a328c271f6f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -139,11 +139,21 @@ public static void uninstallIstio() { * @return ingress port for istio-ingressgateway */ public static int getIstioHttpIngressPort() { + return getIstioHttpIngressPort("http2"); + } + + /** + * Get the http ingress port of istio installation. + * + * @param portName name of port to get + * @return ingress port for istio-ingressgateway + */ + public static int getIstioHttpIngressPort(String portName) { LoggingFacade logger = getLogger(); ExecResult result; StringBuffer getIngressPort; getIngressPort = new StringBuffer(KUBERNETES_CLI + " -n istio-system get service istio-ingressgateway "); - getIngressPort.append("-o jsonpath='{.spec.ports[?(@.name==\"http2\")].nodePort}'"); + getIngressPort.append("-o jsonpath='{.spec.ports[?(@.name==\"" + portName.trim() + "\")].nodePort}'"); logger.info("getIngressPort: " + KUBERNETES_CLI + " command {0}", new String(getIngressPort)); try { result = exec(new String(getIngressPort), true); diff --git a/integration-tests/src/test/resources/istio/istio-productionsecure-tls-mode.yaml b/integration-tests/src/test/resources/istio/istio-productionsecure-tls-mode.yaml new file mode 100644 index 00000000000..18bcc3131b3 --- /dev/null +++ b/integration-tests/src/test/resources/istio/istio-productionsecure-tls-mode.yaml @@ -0,0 +1,82 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +apiVersion: networking.istio.io/v1beta1 +kind: Gateway +metadata: + name: wls-domain-gateway + namespace: NAMESPACE +spec: + selector: + istio: ingressgateway + servers: + - port: + number: 80 + name: http + protocol: HTTP + hosts: + - "*" + - port: + number: 443 + name: https-default-admin + protocol: HTTPS + tls: + mode: PASSTHROUGH + hosts: + - "*" + - port: + number: 9002 + name: https-cluster-admin + protocol: HTTPS + tls: + mode: PASSTHROUGH + hosts: + - "*" + - port: + number: 7002 + name: https-cluster-ssl + protocol: HTTPS + tls: + mode: PASSTHROUGH + hosts: + - "*" +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: wls-domain-virtualservice + namespace: NAMESPACE +spec: + gateways: + - wls-domain-gateway + hosts: + - "*" + tls: + - match: + - port: 443 + sniHosts: + - "*" + route: + - destination: + host: istio-mii-securemode-admin-server.NAMESPACE.svc.cluster.local + port: + number: 9002 + - match: + - port: 9002 + sniHosts: + - "*" + route: + - destination: + host: istio-mii-securemode-cluster-cluster-1.NAMESPACE.svc.cluster.local + port: + number: 9002 + - match: + - port: 7002 + sniHosts: + - "*" + route: + - destination: + host: istio-mii-securemode-cluster-cluster-1.NAMESPACE.svc.cluster.local + port: + number: 7002 + From 02f03ec07cb3502741c9ae920f62d21a7d346560 Mon Sep 17 00:00:00 2001 From: jshum Date: Wed, 7 Aug 2024 10:32:31 -0500 Subject: [PATCH 125/356] Fix restore domain bin and lib logic when preparing mii server - back port from main --- operator/src/main/resources/scripts/modelInImage.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 74aaff70b64..8c1fa284baa 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -1329,14 +1329,17 @@ restoreAppAndLibs() { createFolder "${DOMAIN_HOME}/lib" "This is the './lib' directory within DOMAIN_HOME directory 'domain.spec.domainHome'." || return 1 local WLSDEPLOY_DOMAINLIB="wlsdeploy/domainLibraries" - + local TMP_EXTRACT_LIST="" for file in $(sort_files ${IMG_ARCHIVES_ROOTDIR} "*.zip") do # expand the archive domain libraries to the domain lib, 11 is caution when zip entry doesn't exists cd ${DOMAIN_HOME}/lib || exitOrLoop if [ -f $DOMAIN_BIN_LIB_LIST ] ; then - unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} $(awk '{print $0}' <<< $(grep "wlsdeploy/domainLibraries" $DOMAIN_BIN_LIB_LIST)) + TMP_EXTRACT_LIST=$(awk '{print $0}' <<< $(grep "wlsdeploy/domainLibraries" $DOMAIN_BIN_LIB_LIST)) + if [ -n "$TMP_EXTRACT_LIST" ] ; then + unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} $TMP_EXTRACT_LIST + fi else unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainLibraries/* fi @@ -1351,7 +1354,10 @@ restoreAppAndLibs() { # zip entry doesn't exists cd ${DOMAIN_HOME}/bin || exitOrLoop if [ -f $DOMAIN_BIN_LIB_LIST ] ; then - unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} $(awk '{print $0}' <<< $(grep "wlsdeploy/domainBin" $DOMAIN_BIN_LIB_LIST)) + TMP_EXTRACT_LIST=$(awk '{print $0}' <<< $(grep "wlsdeploy/domainBin" $DOMAIN_BIN_LIB_LIST)) + if [ -n "$TMP_EXTRACT_LIST" ] ; then + unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} $TMP_EXTRACT_LIST + fi else unzip -jo ${IMG_ARCHIVES_ROOTDIR}/${file} wlsdeploy/domainBin/* fi From 10d25727aab566e2afededb66c745d04af33cf9e Mon Sep 17 00:00:00 2001 From: vanajakshi_mukkara Date: Wed, 7 Aug 2024 20:47:40 +0000 Subject: [PATCH 126/356] changing parallel threads to 2 --- Jenkinsfile.oke | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index b59891b0666..bcc387c17e4 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -120,8 +120,8 @@ pipeline { ] ) string(name: 'NUMBER_OF_THREADS', - description: 'Number of threads to run the classes in parallel, default is 3.', - defaultValue: "3" + description: 'Number of threads to run the classes in parallel, default is 2.', + defaultValue: "2" ) string(name: 'CLUSTER_NAME', description: 'OKE cluster name', From c301a228de95ccaf0b870c3ce51ac3095209ea65 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 8 Aug 2024 13:54:07 -0400 Subject: [PATCH 127/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 8eea19e071e..9cd2e8e0173 100644 --- a/pom.xml +++ b/pom.xml @@ -677,7 +677,7 @@ 3.3.1 3.7.1 3.6.0 - 3.3.0 + 3.4.0 10.17.0 1.0 3.4.0 @@ -699,7 +699,7 @@ 1.0.0 3.26.3 2.16.1 - 4.2.1 + 4.2.2 19.0.1 3.0.1u2 2.0.10 @@ -721,7 +721,7 @@ 2.2 2.11.0 10.0.3 - 2.0.13 + 2.0.15 1.5.6 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java From 4fa73e2d3f8d90ba7e40d9dc85719a1ff3879bdc Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 13 Aug 2024 17:51:10 +0000 Subject: [PATCH 128/356] Merge branch 'automate-sample-scripts' into 'main' Add sample scripts zip to release creation See merge request weblogic-cloud/weblogic-kubernetes-operator!4776 (cherry picked from commit 5da40305f6c19d6370ebba3a1af94742543f3cf3) 1049cd82 Add sample scripts zip to release creation --- .github/workflows/release.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 193c383bee6..ca17f1b1d4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,7 +38,11 @@ jobs: - name: Build env: GITHUB_TOKEN: ${{ secrets.PUBLISH_SECRET }} - run: mvn clean package -Dtag=${{ env.VERSION }} + run: | + mvn clean package -Dtag=${{ env.VERSION }} + cd kubernetes/samples/scripts + zip -r ../../../target/sample-scripts.zip * + cd ../../.. - name: Create Draft Release id: draft-release @@ -50,7 +54,8 @@ jobs: --target release/4.2 \ --title 'Operator ${{ env.VERSION }}' \ --repo https://github.com/oracle/weblogic-kubernetes-operator \ - domain-upgrader/target/domain-upgrader.jar + domain-upgrader/target/domain-upgrader.jar \ + target/sample-scripts.zip echo 'EOF' >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 26c7c9091b7817e1a3c371cab39d2bf925703033 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Tue, 13 Aug 2024 20:15:19 +0000 Subject: [PATCH 129/356] Use DB Image to create DB for FMW test suite before DBOperator issue is resolved on OKD --- integration-tests/pom.xml | 4 --- .../kubernetes/ItFmwDomainInPVUsingWDT.java | 34 +++++++++++++----- .../kubernetes/ItFmwDomainInPVUsingWLST.java | 36 ++++++++++++++----- .../weblogic/kubernetes/ItFmwDomainOnPV.java | 27 +++++++------- 4 files changed, 68 insertions(+), 33 deletions(-) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index fbbf18b6bec..1f488cb3567 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -666,10 +666,6 @@ **/ItFmwDomainInPVUsingWLST, **/ItFmwDomainInPVUsingWDT, - **/ItFmwDynamicClusterMiiDomain, - **/ItFmwMiiDomain, - **/ItMiiSampleFmwMain, - **/ItMiiSampleFmwAux, **/ItFmwDomainOnPV diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java index b97d6888dd5..0a4eb3fe93f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java @@ -24,11 +24,13 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.HTTPS_PROXY; import static oracle.weblogic.kubernetes.TestConstants.HTTP_PROXY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.NO_PROXY; +import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -40,6 +42,7 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.DbUtils.setupDBandRCUschema; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.FmwUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; @@ -111,6 +114,11 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(0), "Namespace is null"); dbNamespace = namespaces.get(0); + final int dbListenerPort = getNextFreePort(); + ORACLEDBSUFFIX = ".svc.cluster.local:" + dbListenerPort + "/devpdb.k8s"; + dbUrl = ORACLEDBURLPREFIX + dbNamespace + ORACLEDBSUFFIX; + + // get a new unique opNamespace logger.info("Assign a unique namespace for operator1"); assertNotNull(namespaces.get(1), "Namespace is null"); @@ -123,14 +131,24 @@ public static void initAll(@Namespaces(3) List namespaces) { DOMAINHOMEPREFIX = "/shared/" + domainNamespace + "/domains/"; - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); - createBaseRepoSecret(dbNamespace); - dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); - - logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " - + " dbNamespace: {3}", FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, dbUrl, dbNamespace); - assertDoesNotThrow(() -> createRcuSchema(FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, - dbUrl, dbNamespace)); + if (OKD) { + logger.info("Start DB and create RCU schema for namespace: {0}, dbListenerPort: {1}, RCU prefix: {2}, " + + "dbUrl: {3}, dbImage: {4}, fmwImage: {5} ", dbNamespace, dbListenerPort, RCUSCHEMAPREFIX, dbUrl, + DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC); + assertDoesNotThrow(() -> setupDBandRCUschema(DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC, + RCUSCHEMAPREFIX, dbNamespace, getNextFreePort(), dbUrl, dbListenerPort), + String.format("Failed to create RCU schema for prefix %s in the namespace %s with " + + "dbUrl %s, dbListenerPost %s", RCUSCHEMAPREFIX, dbNamespace, dbUrl, dbListenerPort)); + } else { + logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); + + logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " + + " dbNamespace: {3}", FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, dbUrl, dbNamespace); + assertDoesNotThrow(() -> createRcuSchema(FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, + dbUrl, dbNamespace)); + } // install operator and verify its running in ready state installAndVerifyOperator(opNamespace, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index b8616b28061..52d7b1bf74e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -41,6 +41,7 @@ import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.impl.primitive.Image.getImageEnvVar; @@ -51,6 +52,7 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.DbUtils.setupDBandRCUschema; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; @@ -113,6 +115,11 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(0), "Namespace is null"); dbNamespace = namespaces.get(0); + final int dbListenerPort = getNextFreePort(); + ORACLEDBSUFFIX = ".svc.cluster.local:" + dbListenerPort + "/devpdb.k8s"; + dbUrl = ORACLEDBURLPREFIX + dbNamespace + ORACLEDBSUFFIX; + + logger.info("Assign a unique namespace for operator"); assertNotNull(namespaces.get(1), "Namespace is null"); opNamespace = namespaces.get(1); @@ -121,15 +128,26 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace is null"); jrfDomainNamespace = namespaces.get(2); - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); - createBaseRepoSecret(dbNamespace); - dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); - - logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " - + " dbNamespace: {3}", FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, dbUrl, dbNamespace); - assertDoesNotThrow(() -> createRcuSchema(FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, - dbUrl, dbNamespace)); - + if (OKD) { + logger.info("Start DB and create RCU schema for namespace: {0}, dbListenerPort: {1}, RCU prefix: {2}, " + + "dbUrl: {3}, dbImage: {4}, fmwImage: {5} ", dbNamespace, dbListenerPort, RCUSCHEMAPREFIX, dbUrl, + DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC); + assertDoesNotThrow(() -> setupDBandRCUschema(DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC, + RCUSCHEMAPREFIX, dbNamespace, getNextFreePort(), dbUrl, dbListenerPort), + String.format("Failed to create RCU schema for prefix %s in the namespace %s with " + + "dbUrl %s, dbListenerPost %s", RCUSCHEMAPREFIX, dbNamespace, dbUrl, dbListenerPort)); + + } else { + logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); + + logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " + + " dbNamespace: {3}", FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, dbUrl, dbNamespace); + assertDoesNotThrow(() -> createRcuSchema(FMWINFRA_IMAGE_TO_USE_IN_SPEC, RCUSCHEMAPREFIX, + dbUrl, dbNamespace)); + } + // install operator and verify its running in ready state installAndVerifyOperator(opNamespace, jrfDomainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index 46dc2322728..26f58a1becb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -39,6 +39,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.ELASTICSEARCH_HOST; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; @@ -66,6 +67,7 @@ import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; +import static oracle.weblogic.kubernetes.utils.DbUtils.startOracleDB; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.DomainUtils.deleteDomainResource; @@ -143,18 +145,19 @@ public static void initAll(@Namespaces(3) List namespaces) { DOMAINHOMEPREFIX = "/shared/" + domainNamespace + "/domains/"; - //install Oracle Database Operator - String dbName = "fmwdomainonpv1" + "my-oracle-db"; - logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); - createBaseRepoSecret(dbNamespace); - dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); - /* - logger.info("Start DB in namespace: {0}, dbListenerPort: {1}, dbUrl: {2}, dbImage: {3}", - dbNamespace, dbListenerPort, dbUrl, DB_IMAGE_TO_USE_IN_SPEC); - assertDoesNotThrow(() -> startOracleDB(DB_IMAGE_TO_USE_IN_SPEC, getNextFreePort(), dbNamespace, dbListenerPort), - String.format("Failed to start Oracle DB in the namespace %s with dbUrl %s, dbListenerPost %s", - dbNamespace, dbUrl, dbListenerPort)); - */ + if (OKD) { + logger.info("Start DB in namespace: {0}, dbListenerPort: {1}, dbUrl: {2}, dbImage: {3}", + dbNamespace, dbListenerPort, dbUrl, DB_IMAGE_TO_USE_IN_SPEC); + assertDoesNotThrow(() -> startOracleDB(DB_IMAGE_TO_USE_IN_SPEC, getNextFreePort(), dbNamespace, dbListenerPort), + String.format("Failed to start Oracle DB in the namespace %s with dbUrl %s, dbListenerPost %s", + dbNamespace, dbUrl, dbListenerPort)); + } else { + String dbName = "fmwdomainonpv1" + "my-oracle-db"; + logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); + + } // install operator and verify its running in ready state HelmParams opHelmParams = new HelmParams().releaseName(OPERATOR_RELEASE_NAME) From 7e7a99dddf15b32f8321083ead9b287163f65d99 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 13 Aug 2024 20:26:20 +0000 Subject: [PATCH 130/356] Cherry-pick Sankar's changes to release/4.2 --- Jenkinsfile.podman | 51 ++- Jenkinsfile.podman.upgrade | 668 +++++++++++++++++++++++++++++++++++++ 2 files changed, 691 insertions(+), 28 deletions(-) create mode 100644 Jenkinsfile.podman.upgrade diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index a6681d9a2b2..0c1c73abc12 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -2,27 +2,24 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // def kind_k8s_map = [ - '0.22.0': [ - '1.29.2': 'kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245', - '1.29': 'kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245', - '1.28.7': 'kindest/node:v1.28.7@sha256:9bc6c451a289cf96ad0bbaf33d416901de6fd632415b076ab05f5fa7e4f65c58', - '1.28': 'kindest/node:v1.28.7@sha256:9bc6c451a289cf96ad0bbaf33d416901de6fd632415b076ab05f5fa7e4f65c58', - '1.27.11': 'kindest/node:v1.27.11@sha256:681253009e68069b8e01aad36a1e0fa8cf18bb0ab3e5c4069b2e65cafdd70843', - '1.27': 'kindest/node:v1.27.11@sha256:681253009e68069b8e01aad36a1e0fa8cf18bb0ab3e5c4069b2e65cafdd70843', - '1.26.14': 'kindest/node:v1.26.14@sha256:5d548739ddef37b9318c70cb977f57bf3e5015e4552be4e27e57280a8cbb8e4f', - '1.26': 'kindest/node:v1.26.14@sha256:5d548739ddef37b9318c70cb977f57bf3e5015e4552be4e27e57280a8cbb8e4f', - '1.25.16': 'kindest/node:v1.25.16@sha256:e8b50f8e06b44bb65a93678a65a26248fae585b3d3c2a669e5ca6c90c69dc519', - '1.25': 'kindest/node:v1.25.16@sha256:e8b50f8e06b44bb65a93678a65a26248fae585b3d3c2a669e5ca6c90c69dc519', - '1.24.17': 'kindest/node:v1.24.17@sha256:bad10f9b98d54586cba05a7eaa1b61c6b90bfc4ee174fdc43a7b75ca75c95e51', - '1.24': 'kindest/node:v1.24.17@sha256:bad10f9b98d54586cba05a7eaa1b61c6b90bfc4ee174fdc43a7b75ca75c95e51', - '1.23.17': 'kindest/node:v1.23.17@sha256:14d0a9a892b943866d7e6be119a06871291c517d279aedb816a4b4bc0ec0a5b3', - '1.23': 'kindest/node:v1.23.17@sha256:14d0a9a892b943866d7e6be119a06871291c517d279aedb816a4b4bc0ec0a5b3' + '0.23.0': [ + '1.30.0': 'kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e', + '1.30': 'kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e', + '1.29.4': 'kindest/node:v1.29.4@sha256:3abb816a5b1061fb15c6e9e60856ec40d56b7b52bcea5f5f1350bc6e2320b6f8', + '1.29': 'kindest/node:v1.29.4@sha256:3abb816a5b1061fb15c6e9e60856ec40d56b7b52bcea5f5f1350bc6e2320b6f8', + '1.28.9': 'kindest/node:v1.28.9@sha256:dca54bc6a6079dd34699d53d7d4ffa2e853e46a20cd12d619a09207e35300bd0', + '1.28': 'kindest/node:v1.28.9@sha256:dca54bc6a6079dd34699d53d7d4ffa2e853e46a20cd12d619a09207e35300bd0', + '1.27.13': 'kindest/node:v1.27.13@sha256:17439fa5b32290e3ead39ead1250dca1d822d94a10d26f1981756cd51b24b9d8', + '1.27': 'kindest/node:v1.27.13@sha256:17439fa5b32290e3ead39ead1250dca1d822d94a10d26f1981756cd51b24b9d8', + '1.26.15': 'kindest/node:v1.26.15@sha256:84333e26cae1d70361bb7339efb568df1871419f2019c80f9a12b7e2d485fe19', + '1.26': 'kindest/node:v1.26.15@sha256:84333e26cae1d70361bb7339efb568df1871419f2019c80f9a12b7e2d485fe19', + '1.25.16': 'kindest/node:v1.25.16@sha256:5da57dfc290ac3599e775e63b8b6c49c0c85d3fec771cd7d55b45fae14b38d3b', + '1.25': 'kindest/node:v1.25.16@sha256:5da57dfc290ac3599e775e63b8b6c49c0c85d3fec771cd7d55b45fae14b38d3b' ] ] def _kind_image = null CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential - H 3 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.17''' + H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential''' pipeline { agent { label 'large-ol9u4' } @@ -85,27 +82,25 @@ pipeline { choice(name: 'KIND_VERSION', description: 'Kind version.', choices: [ - '0.22.0' + '0.23.0' ] ) choice(name: 'KUBE_VERSION', - description: 'Kubernetes version. Supported values depend on the Kind version. Kind 0.22.0: 1.29, 1.29.2, 1.28, 1.28.7, 1.27, 1.27.11, 1.26, 1.26.14, 1.25, 1.25.16, 1.24, 1.24.17, 1.23, 1.23.17 ', + description: 'Kubernetes version. Supported values depend on the Kind version. Kind 0.23.0: 1.30, 1.30.0, 1.29, 1.29.4, 1.28, 1.28.9, 1.27, 1.27.13, 1.26, 1.26.15, 1.25, 1.25.16 ', choices: [ // The first item in the list is the default value... - '1.29.2', + '1.30.0', + '1.30', + '1.29.4', '1.29', - '1.28.7', + '1.28.9', '1.28', - '1.27.11', + '1.27.13', '1.27', - '1.26.14', + '1.26.15', '1.26', '1.25.16', - '1.25', - '1.24.17', - '1.24', - '1.23.17', - '1.23' + '1.25' ] ) string(name: 'HELM_VERSION', diff --git a/Jenkinsfile.podman.upgrade b/Jenkinsfile.podman.upgrade new file mode 100644 index 00000000000..5d44637d3c3 --- /dev/null +++ b/Jenkinsfile.podman.upgrade @@ -0,0 +1,668 @@ +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// +def kind_k8s_map = [ + '0.22.0': [ + '1.29.2': 'kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245', + '1.29': 'kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245', + '1.28.7': 'kindest/node:v1.28.7@sha256:9bc6c451a289cf96ad0bbaf33d416901de6fd632415b076ab05f5fa7e4f65c58', + '1.28': 'kindest/node:v1.28.7@sha256:9bc6c451a289cf96ad0bbaf33d416901de6fd632415b076ab05f5fa7e4f65c58', + '1.27.11': 'kindest/node:v1.27.11@sha256:681253009e68069b8e01aad36a1e0fa8cf18bb0ab3e5c4069b2e65cafdd70843', + '1.27': 'kindest/node:v1.27.11@sha256:681253009e68069b8e01aad36a1e0fa8cf18bb0ab3e5c4069b2e65cafdd70843', + '1.26.14': 'kindest/node:v1.26.14@sha256:5d548739ddef37b9318c70cb977f57bf3e5015e4552be4e27e57280a8cbb8e4f', + '1.26': 'kindest/node:v1.26.14@sha256:5d548739ddef37b9318c70cb977f57bf3e5015e4552be4e27e57280a8cbb8e4f', + '1.25.16': 'kindest/node:v1.25.16@sha256:e8b50f8e06b44bb65a93678a65a26248fae585b3d3c2a669e5ca6c90c69dc519', + '1.25': 'kindest/node:v1.25.16@sha256:e8b50f8e06b44bb65a93678a65a26248fae585b3d3c2a669e5ca6c90c69dc519', + '1.24.17': 'kindest/node:v1.24.17@sha256:bad10f9b98d54586cba05a7eaa1b61c6b90bfc4ee174fdc43a7b75ca75c95e51', + '1.24': 'kindest/node:v1.24.17@sha256:bad10f9b98d54586cba05a7eaa1b61c6b90bfc4ee174fdc43a7b75ca75c95e51', + '1.23.17': 'kindest/node:v1.23.17@sha256:14d0a9a892b943866d7e6be119a06871291c517d279aedb816a4b4bc0ec0a5b3', + '1.23': 'kindest/node:v1.23.17@sha256:14d0a9a892b943866d7e6be119a06871291c517d279aedb816a4b4bc0ec0a5b3' + ] +] +def _kind_image = null +CRON_SETTINGS = '''H 4 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.17''' + +pipeline { + agent { label 'large-ol9u4' } + options { + timeout(time: 800, unit: 'MINUTES') + } + + triggers { + // timer trigger for "nightly build" + parameterizedCron(env.JOB_NAME == 'wko-kind-main-nightly-podman-upgrade' ? + CRON_SETTINGS : '') + } + + tools { + maven 'maven-3.8.7' + jdk 'jdk21' + } + + environment { + ocir_host = "${env.WKT_OCIR_HOST}" + wko_tenancy = "${env.WKT_TENANCY}" + ocir_creds = 'wkt-ocir-creds' + + outdir = "${WORKSPACE}/staging" + result_root = "${outdir}/wl_k8s_test_results" + pv_root = "${outdir}/k8s-pvroot" + kubeconfig_file = "${result_root}/kubeconfig" + + kind_name = "kind" + kind_network = "kind" + registry_name = "kind-registry" + registry_host = "${registry_name}" + registry_port = "5000" + + start_time = sh(script: 'date +"%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" + } + + parameters { + string(name: 'BRANCH', + description: 'The branch to run the tests on', + defaultValue: 'main' + ) + + choice(name: 'MAVEN_PROFILE_NAME', + description: 'Profile to use in mvn command to run the tests. Possible values are wls-srg (the default), integration-tests, toolkits-srg, kind-sequential and kind-upgrade. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + choices: [ + 'kind-parallel', + 'wls-srg', + 'integration-tests', + 'kind-sequential', + 'kind-upgrade', + 'toolkits-srg' + ] + ) + string(name: 'IT_TEST', + description: 'Comma separated list of individual It test classes to be run e.g., ItParameterizedDomain, ItMiiUpdateDomainConfig, ItMiiDynamicUpdate*, ItMiiMultiMode', + defaultValue: '' + ) + string(name: 'OPERATOR_LOG_LEVEL', + description: 'The default log level is not set', + defaultValue: '' + ) + choice(name: 'KIND_VERSION', + description: 'Kind version.', + choices: [ + '0.22.0' + ] + ) + choice(name: 'KUBE_VERSION', + description: 'Kubernetes version. Supported values depend on the Kind version. Kind 0.22.0: 1.29, 1.29.2, 1.28, 1.28.7, 1.27, 1.27.11, 1.26, 1.26.14, 1.25, 1.25.16, 1.24, 1.24.17, 1.23, 1.23.17 ', + choices: [ + // The first item in the list is the default value... + '1.29.2', + '1.29', + '1.28.7', + '1.28', + '1.27.11', + '1.27', + '1.26.4', + '1.26', + '1.25.16', + '1.25', + '1.24.17', + '1.24', + '1.23.17', + '1.23' + ] + ) + string(name: 'HELM_VERSION', + description: 'Helm version', + defaultValue: '3.11.2' + ) + choice(name: 'ISTIO_VERSION', + description: 'Istio version', + choices: [ + '1.17.2', + '1.16.1', + '1.13.2', + '1.12.6', + '1.11.1', + '1.10.4', + '1.9.9' + ] + ) + booleanParam(name: 'PARALLEL_RUN', + description: 'Runs tests in parallel. Default is true, test classes run in parallel.', + defaultValue: true + ) + string(name: 'NUMBER_OF_THREADS', + description: 'Number of threads to run the classes in parallel, default is 3.', + defaultValue: "3" + ) + string(name: 'WDT_DOWNLOAD_URL', + description: 'URL to download WDT.', + defaultValue: 'https://github.com/oracle/weblogic-deploy-tooling/releases/latest' + ) + string(name: 'WIT_DOWNLOAD_URL', + description: 'URL to download WIT.', + defaultValue: 'https://github.com/oracle/weblogic-image-tool/releases/latest' + ) + string(name: 'REMOTECONSOLE_VERSION', + description: 'RemoteConsole version.', + defaultValue: '2.4.7' + ) + string(name: 'TEST_IMAGES_REPO', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + choice(name: 'BASE_IMAGES_REPO', + choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], + description: 'Repository to pull the base images. Make sure to modify the image names if you are modifying this parameter value.' + ) + string(name: 'WEBLOGIC_IMAGE_NAME', + description: 'WebLogic base image name. Default is the image name in BASE_IMAGES_REPO. Use middleware/weblogic for OCR.', + defaultValue: "test-images/weblogic" + ) + string(name: 'WEBLOGIC_IMAGE_TAG', + description: '12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '12.2.1.4' + ) + string(name: 'FMWINFRA_IMAGE_NAME', + description: 'FWM Infra image name. Default is the image name in BASE_IMAGES_REPO. Use middleware/fmw-infrastructure for OCR.', + defaultValue: "test-images/fmw-infrastructure" + ) + string(name: 'FMWINFRA_IMAGE_TAG', + description: 'FWM Infra image tag', + defaultValue: '12.2.1.4' + ) + string(name: 'DB_IMAGE_NAME', + description: 'Oracle DB image name. Default is the image name in BASE_IMAGES_REPO, use database/enterprise for OCR.', + defaultValue: "test-images/database/enterprise" + ) + string(name: 'DB_IMAGE_TAG', + description: 'Oracle DB image tag', + defaultValue: '12.2.0.1-slim' + ) + string(name: 'MONITORING_EXPORTER_BRANCH', + description: '', + defaultValue: 'main' + ) + string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', + description: '', + defaultValue: '2.1.2' + ) + string(name: 'PROMETHEUS_CHART_VERSION', + description: '', + defaultValue: '17.0.0' + ) + string(name: 'GRAFANA_CHART_VERSION', + description: '', + defaultValue: '6.38.6' + ) + booleanParam(name: 'COLLECT_LOGS_ON_SUCCESS', + description: 'Collect logs for successful runs. Default is false.', + defaultValue: false + ) + } + + stages { + stage('Prepare the environment and run integration tests') { + stages { + stage('Workaround JENKINS-41929 Parameters bug') { + steps { + echo 'Initialize parameters as environment variables due to https://issues.jenkins-ci.org/browse/JENKINS-41929' + evaluate """${def script = ""; params.each { k, v -> script += "env.${k} = '''${v}'''\n" }; return script}""" + } + } + stage ('Echo environment') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + env|sort + java -version + mvn --version + python --version + podman version + ulimit -a + ulimit -aH + ''' + script { + def knd = params.KIND_VERSION + def k8s = params.KUBE_VERSION + if (knd != null && k8s != null) { + def k8s_map = kind_k8s_map.get(knd) + if (k8s_map != null) { + _kind_image = k8s_map.get(k8s) + } + if (_kind_image == null) { + currentBuild.result = 'ABORTED' + error('Unable to compute _kind_image for Kind version ' + + knd + ' and Kubernetes version ' + k8s) + } + } else { + currentBuild.result = 'ABORTED' + error('KIND_VERSION or KUBE_VERSION were null') + } + echo "Kind Image = ${_kind_image}" + if (env.JOB_NAME == 'wko-kind-k8sversion') { + currentBuild.description = "${GIT_BRANCH} ${KUBE_VERSION}" + } else { + currentBuild.description = "${GIT_BRANCH} ${MAVEN_PROFILE_NAME}" + } + } + + } + } + + stage('Build WebLogic Kubernetes Operator') { + steps { + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + sh "mvn -DtrimStackTrace=false clean install" + } + } + } + + stage('Make Workspace bin directory') { + steps { + sh "mkdir -m777 -p ${WORKSPACE}/bin" + } + } + + stage('Install Helm') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=helm/helm-v${HELM_VERSION}.tar.gz --file=helm.tar.gz \ + --auth=instance_principal + tar zxf helm.tar.gz + mv linux-amd64/helm ${WORKSPACE}/bin/helm + rm -rf linux-amd64 + helm version + ''' + } + } + + stage('Run Helm installation tests') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + sh 'export PATH=${runtime_path} && mvn -pl kubernetes -P helm-installation-test verify' + } + } + } + + stage ('Install kubectl') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + KUBE_VERSION = "${params.KUBE_VERSION}" + } + steps { + sh ''' + export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=kubectl/kubectl-v${KUBE_VERSION} --file=${WORKSPACE}/bin/kubectl \ + --auth=instance_principal + chmod +x ${WORKSPACE}/bin/kubectl + kubectl version --client=true + ''' + } + } + + stage('Install kind') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=kind/kind-v${KIND_VERSION} --file=${WORKSPACE}/bin/kind \ + --auth=instance_principal + chmod +x "${WORKSPACE}/bin/kind" + kind version + ''' + } + } + + stage('Preparing Integration Test Environment') { + steps { + sh 'mkdir -m777 -p ${result_root}' + echo "Results will be in ${result_root}" + sh 'mkdir -m777 -p ${pv_root}' + echo "Persistent volume files, if any, will be in ${pv_root}" + } + } + + stage('Start registry container') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + + running="$(podman container inspect -f '{{.State.Running}}' "${registry_name}" 2>/dev/null || true)" + if [ "${running}" = 'true' ]; then + echo "Stopping the registry container ${registry_name}" + podman stop "${registry_name}" + podman rm --force "${registry_name}" + fi + + podman run -d --restart=always -p "127.0.0.1:${registry_port}:5000" --name "${registry_name}" \ + ${ocir_host}/${wko_tenancy}/test-images/docker/registry:2.8.2 + echo "Registry Host: ${registry_host}" + ''' + } + } + + stage('Create kind cluster') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + kind_image = sh(script: "echo -n ${_kind_image}", returnStdout: true).trim() + } + steps { + sh ''' + export PATH=${runtime_path} + export KIND_EXPERIMENTAL_PROVIDER=podman + + podman version + cat /etc/systemd/system/user@.service.d/delegate.conf + cat /etc/modules-load.d/iptables.conf + lsmod|grep -E "^ip_tables|^iptable_filter|^iptable_nat|^ip6" + + if kind delete cluster --name ${kind_name} --kubeconfig "${kubeconfig_file}"; then + echo "Deleted orphaned kind cluster ${kind_name}" + fi + # settings needed by elastic logging tests + echo "running sudo sysctl -w vm.max_map_count=262144" + sudo sysctl -w vm.max_map_count=262144 + cat <> ${WORKSPACE}/.mvn/maven.config + fi + echo "-Dwko.it.wle.download.url=\"${wle_download_url}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.result.root=\"${result_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.pv.root=\"${pv_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.k8s.nodeport.host=\"${K8S_NODEPORT_HOST}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.kind.repo=\"localhost:${registry_port}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.istio.version=\"${ISTIO_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DPARALLEL_CLASSES=\"${PARALLEL_RUN}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DNUMBER_OF_THREADS=\"${NUMBER_OF_THREADS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wdt.download.url=\"${WDT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wit.download.url=\"${WIT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.repo=\"${BASE_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.repo=\"${TEST_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.name=\"${WEBLOGIC_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.tag=\"${WEBLOGIC_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.name=\"${FMWINFRA_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.tag=\"${FMWINFRA_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.name=\"${DB_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.tag=\"${DB_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.branch=\"${MONITORING_EXPORTER_BRANCH}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.webapp.version=\"${MONITORING_EXPORTER_WEBAPP_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.grafana.chart.version=\"${GRAFANA_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.collect.logs.on.success=\"${COLLECT_LOGS_ON_SUCCESS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DWLSIMG_BUILDER=\"podman\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Djdk.httpclient.allowRestrictedHeaders=\"host\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DOPERATOR_LOG_LEVEL=\"${OPERATOR_LOG_LEVEL}\"" >> ${WORKSPACE}/.mvn/maven.config + + echo "${WORKSPACE}/.mvn/maven.config contents:" + cat "${WORKSPACE}/.mvn/maven.config" + cp "${WORKSPACE}/.mvn/maven.config" "${result_root}" + ''' + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + withCredentials([ + usernamePassword(credentialsId: "${ocir_creds}", usernameVariable: 'OCIR_USER', passwordVariable: 'OCIR_PASS') + ]) { + sh ''' + export PATH=${runtime_path} + export KUBECONFIG=${kubeconfig_file} + export BASE_IMAGES_REPO_USERNAME="${OCIR_USER}" + export BASE_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export BASE_IMAGES_REPO_EMAIL="noreply@oracle.com" + export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" + export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" + if ! time mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/kindtest.log"; then + echo "integration-tests failed" + exit 1 + fi + ''' + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + export KUBECONFIG=${kubeconfig_file} + mkdir -m777 -p ${result_root}/kubelogs + if ! kind export logs "${result_root}/kubelogs" --name "${kind_name}" --verbosity 99; then + echo "Failed to export kind logs for kind cluster ${kind_name}" + fi + if ! podman exec kind-worker journalctl --utc --dmesg --system > "${result_root}/journalctl-kind-worker.out"; then + echo "Failed to run journalctl for kind worker" + fi + if ! podman exec kind-control-plane journalctl --utc --dmesg --system > "${result_root}/journalctl-kind-control-plane.out"; then + echo "Failed to run journalctl for kind control plane" + fi + if ! journalctl --utc --dmesg --system --since "$start_time" > "${result_root}/journalctl-compute.out"; then + echo "Failed to run journalctl for compute node" + fi + + mkdir -m777 -p "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + sudo mv -f ${result_root}/* "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + ''' + archiveArtifacts(artifacts: + "logdir/${BUILD_TAG}/wl_k8s_test_results/diagnostics/**/*,logdir/${BUILD_TAG}/wl_k8s_test_results/workdir/liftandshiftworkdir/**/*,integration-tests/target/failsafe-reports/*.xml") + junit(testResults: 'integration-tests/target/failsafe-reports/*.xml', allowEmptyResults: true) + } + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + running="$(podman container inspect -f '{{.State.Running}}' "${registry_name}" 2>/dev/null || true)" + if [ "${running}" = 'true' ]; then + echo "Stopping the registry container ${registry_name}" + podman stop "${registry_name}" + podman rm --force "${registry_name}" + fi + echo 'Remove old Kind cluster (if any)...' + if ! kind delete cluster --name ${kind_name} --kubeconfig "${kubeconfig_file}"; then + echo "Failed to delete kind cluster ${kind_name}" + fi + sudo chown -R $(whoami) ${WORKSPACE} + ''' + } + } + } + stage ('Sync') { + when { + anyOf { + branch 'main' + branch 'release/4.0' + branch 'release/3.4' + } + anyOf { + not { triggeredBy 'TimerTrigger' } + tag 'v*' + } + } + steps { + build job: "wkt-sync", parameters: [ string(name: 'REPOSITORY', value: 'weblogic-kubernetes-operator') ] + } + } + } +} From 6353018575ddfe5df912162afc04075163fd11fb Mon Sep 17 00:00:00 2001 From: xian_cao Date: Thu, 15 Aug 2024 00:13:01 +0000 Subject: [PATCH 131/356] remove ItIntrospectVersion and ItKubernetesDomainEvents from crio-pipeline in release/4.2 --- .../java/oracle/weblogic/kubernetes/ItIntrospectVersion.java | 1 - .../oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java | 1 - 2 files changed, 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index a3035c855ff..35a8c3a5b8a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -284,7 +284,6 @@ public static void initAll(@Namespaces(3) List namespaces) { @Test @DisplayName("Test introSpectVersion starting a introspector and updating domain status") @Tag("gate") - @Tag("crio") void testDomainIntrospectVersionNotRolling() { // get the pod creation time stamps Map pods = new LinkedHashMap<>(); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index 34e619fc9bb..53767bd9f2b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -237,7 +237,6 @@ public static void initAll(@Namespaces(6) List namespaces) { @Test @DisplayName("Test domain events for various successful domain life cycle changes") @Tag("gate") - @Tag("crio") void testK8SEventsSuccess() { OffsetDateTime timestamp = now(); logger.info("Creating domain"); From 4950d70205cb7c707355b031e4b29a7c09d2181c Mon Sep 17 00:00:00 2001 From: Sankar Neelakandan Date: Thu, 15 Aug 2024 08:06:34 -0700 Subject: [PATCH 132/356] change the default branch value to 4.2 --- Jenkinsfile.podman.upgrade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.podman.upgrade b/Jenkinsfile.podman.upgrade index 5d44637d3c3..2160dab31d7 100644 --- a/Jenkinsfile.podman.upgrade +++ b/Jenkinsfile.podman.upgrade @@ -62,7 +62,7 @@ pipeline { parameters { string(name: 'BRANCH', description: 'The branch to run the tests on', - defaultValue: 'main' + defaultValue: 'release/4.2' ) choice(name: 'MAVEN_PROFILE_NAME', From 8c74420dbc8f38a3c1cb6337c3bb972505ca6e12 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sun, 18 Aug 2024 12:42:44 -0400 Subject: [PATCH 133/356] Make Istio default version 1.23.0 --- Jenkinsfile.podman | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 0c1c73abc12..8bf873e8b8d 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -110,6 +110,7 @@ pipeline { choice(name: 'ISTIO_VERSION', description: 'Istio version', choices: [ + '1.23.0', '1.17.2', '1.16.1', '1.13.2', From 2d944201f29edd7809313b63b15dee3115e88323 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 14 Aug 2024 20:02:30 +0000 Subject: [PATCH 134/356] Merge branch 'update-aks-sample' into 'main' Update AKS sample See merge request weblogic-cloud/weblogic-kubernetes-operator!4779 (cherry picked from commit f79af4a1652ece7dd394114f9445f84cc77d0d50) 9a79487e modify model in image sample. ff0a15ae modify domain on PV sample 1f9ba6e6 Merge branch 'oracle:main' into update-aks-sample --- .../azure-kubernetes-service/domain-on-pv.md | 53 +++++++++---------- .../includes/download-samples-zip.txt | 4 +- .../model-in-image.md | 10 ++-- .../create-domain-on-aks.sh | 2 +- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md index 4c2284fd981..5e1ff1c7e91 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md +++ b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md @@ -10,7 +10,6 @@ This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://o #### Contents - [Prerequisites](#prerequisites) - - [Prepare Parameters](#prepare-parameters) - [Create Resource Group](#create-resource-group) - [Create an AKS cluster](#create-the-aks-cluster) - [Create and configure storage](#create-storage) @@ -31,6 +30,31 @@ This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://o {{< readfile file="/samples/azure-kubernetes-service/includes/prerequisites-01.txt" >}} +##### Prepare parameters + +```shell +# Change these parameters as needed for your own environment +export ORACLE_SSO_EMAIL= +export ORACLE_SSO_PASSWORD="" + +# Specify a prefix to name resources, only allow lowercase letters and numbers, between 1 and 7 characters +export BASE_DIR=~ +export NAME_PREFIX=wls +export WEBLOGIC_USERNAME=weblogic +export WEBLOGIC_PASSWORD=Secret123456 +export domainUID=domain1 +# Used to generate resource names. +export TIMESTAMP=`date +%s` +export AKS_CLUSTER_NAME="${NAME_PREFIX}aks${TIMESTAMP}" +export AKS_PERS_RESOURCE_GROUP="${NAME_PREFIX}resourcegroup${TIMESTAMP}" +export AKS_PERS_LOCATION=eastus +export AKS_PERS_STORAGE_ACCOUNT_NAME="${NAME_PREFIX}storage${TIMESTAMP}" +export AKS_PERS_SHARE_NAME="${NAME_PREFIX}-weblogic-${TIMESTAMP}" +export SECRET_NAME_DOCKER="${NAME_PREFIX}regcred" +export ACR_NAME="${NAME_PREFIX}acr${TIMESTAMP}" + +``` + {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt" >}} ##### Sign in with Azure CLI @@ -56,38 +80,13 @@ The steps in this section show you how to sign in to the Azure CLI. 1. Set the subscription ID. Be sure to replace the placeholder with the appropriate value. ```shell - $ export SUBSCRIPTION_ID= + $ export SUBSCRIPTION_ID=$(az account show --query id --output tsv) $ az account set -s $SUBSCRIPTION_ID ``` {{% notice info %}} The following sections of the sample instructions will guide you, step-by-step, through the process of setting up a WebLogic cluster on AKS - remaining as close as possible to a native Kubernetes experience. This lets you understand and customize each step. If you wish to have a more automated experience that abstracts some lower level details, you can skip to the [Automation](#automation) section. {{% /notice %}} -#### Prepare parameters - -```shell -# Change these parameters as needed for your own environment -export ORACLE_SSO_EMAIL= -export ORACLE_SSO_PASSWORD= - -# Specify a prefix to name resources, only allow lowercase letters and numbers, between 1 and 7 characters -export BASE_DIR=~ -export NAME_PREFIX=wls -export WEBLOGIC_USERNAME=weblogic -export WEBLOGIC_PASSWORD=Secret123456 -export domainUID=domain1 -# Used to generate resource names. -export TIMESTAMP=`date +%s` -export AKS_CLUSTER_NAME="${NAME_PREFIX}aks${TIMESTAMP}" -export AKS_PERS_RESOURCE_GROUP="${NAME_PREFIX}resourcegroup${TIMESTAMP}" -export AKS_PERS_LOCATION=eastus -export AKS_PERS_STORAGE_ACCOUNT_NAME="${NAME_PREFIX}storage${TIMESTAMP}" -export AKS_PERS_SHARE_NAME="${NAME_PREFIX}-weblogic-${TIMESTAMP}" -export SECRET_NAME_DOCKER="${NAME_PREFIX}regcred" -export ACR_NAME="${NAME_PREFIX}acr${TIMESTAMP}" - -``` - {{< readfile file="/samples/azure-kubernetes-service/includes/download-samples-zip.txt" >}} {{< readfile file="/samples/azure-kubernetes-service/includes/create-resource-group.txt" >}} diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt index 47538eb49d7..3e4b1fefe4d 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt @@ -1,11 +1,11 @@ ##### Download the WebLogic Kubernetes Operator sample. -Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.1.8, but should work with the latest release. +Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.2.5, but should work with the latest release. ```shell $ cd $BASE_DIR $ mkdir sample-scripts -$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/releases/download/v4.1.8/sample-scripts.zip \ +$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/releases/download/v4.2.5/sample-scripts.zip \ -o ${BASE_DIR}/sample-scripts/sample-scripts.zip $ unzip ${BASE_DIR}/sample-scripts/sample-scripts.zip -d ${BASE_DIR}/sample-scripts ``` diff --git a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md index 3064df74ca8..d8f0bd1c5cc 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md +++ b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md @@ -99,7 +99,7 @@ Update the repo to get the latest Helm charts. It is a best practice to do this $ helm repo update $ helm install weblogic-operator weblogic-operator/weblogic-operator \ --namespace sample-weblogic-operator-ns \ - --version 4.1.8 \ + --version 4.2.5 \ --set serviceAccount=sample-weblogic-operator-sa \ --wait ``` @@ -115,7 +115,7 @@ REVISION: 1 TEST SUITE: None ``` -{{% notice tip %}} If you wish to use a more recent version of the operator, replace the `4.1.8` in the preceding command with the other version number. To see the list of versions, visit the [GitHub releases page](https://github.com/oracle/weblogic-kubernetes-operator/releases). +{{% notice tip %}} If you wish to use a more recent version of the operator, replace the `4.2.5` in the preceding command with the other version number. To see the list of versions, visit the [GitHub releases page](https://github.com/oracle/weblogic-kubernetes-operator/releases). {{% /notice %}} @@ -126,7 +126,7 @@ $ helm list -A ``` ``` NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -weblogic-operator sample-weblogic-operator-ns 1 2023-05-15 10:31:05.1890341 +0800 CST deployeweblogic-operator-4.1.8 4.1.8 +weblogic-operator sample-weblogic-operator-ns 1 2023-05-15 10:31:05.1890341 +0800 CST deployeweblogic-operator-4.2.5 4.2.5 ``` ```shell $ kubectl get pods -n sample-weblogic-operator-ns @@ -696,7 +696,7 @@ Events: Access the Administration Console using the admin load balancer IP address. ```shell -$ ADMIN_SERVER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-admin-server-external-lb -o=jsonpath='{.status.loadBalancer.ingress\[0\].ip}') +$ ADMIN_SERVER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-admin-server-external-lb -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') $ echo "Administration Console Address: http://${ADMIN_SERVER_IP}:7001/console/" ``` @@ -704,7 +704,7 @@ Access the sample application using the cluster load balancer IP address. ```shell ## Access the sample application using the cluster load balancer IP. -$ CLUSTER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-cluster-1-lb -o=jsonpath='{.status.loadBalancer.ingress\[0\].ip}') +$ CLUSTER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-cluster-1-lb -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') ``` ```shell diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh index b7237c0298f..0a19b37bb63 100755 --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh +++ b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh @@ -199,7 +199,7 @@ initialize() { # Generate Azure resource name - export image_build_branch_name="v4.1.1" + export image_build_branch_name="v4.2.5" export image_build_base_dir="/tmp/tmp${azureResourceUID}" export acr_account_name=${namePrefix}acr${azureResourceUID} From e441a6b824c5c4b66ec3e984a69561b2173ae06a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 21 Aug 2024 16:47:34 -0400 Subject: [PATCH 135/356] Move to Hugo 0.133.0 --- .github/workflows/publish-latest-minor-gh-pages.yml | 3 ++- .../accessing-the-domain/weblogic-admin-console.md | 8 ++++---- .../_index.md => managing-fmw-domains.md} | 0 3 files changed, 6 insertions(+), 5 deletions(-) rename documentation/site/content/managing-domains/{managing-fmw-domains/_index.md => managing-fmw-domains.md} (100%) diff --git a/.github/workflows/publish-latest-minor-gh-pages.yml b/.github/workflows/publish-latest-minor-gh-pages.yml index bc940b69291..cfd80e12a08 100644 --- a/.github/workflows/publish-latest-minor-gh-pages.yml +++ b/.github/workflows/publish-latest-minor-gh-pages.yml @@ -38,7 +38,7 @@ jobs: - name: Build and publish site run: | - curl -fL -o hugo.tar.gz "https://github.com/gohugoio/hugo/releases/download/v0.108.0/hugo_0.108.0_Linux-64bit.tar.gz" + curl -fL -o hugo.tar.gz "https://github.com/gohugoio/hugo/releases/download/v0.133.0/hugo_0.133.0_Linux-64bit.tar.gz" tar -xf hugo.tar.gz export PATH="$PWD:$PATH" mkdir $GITHUB_WORKSPACE/WORK @@ -50,6 +50,7 @@ jobs: latest_tag=$(git ls-remote https://github.com/oracle/weblogic-kubernetes-operator.git --h --sort origin "refs/tags/v*" | cut -f2 | grep v4.2 | tail -1 | cut -c12-) echo "Latest tag is $latest_tag..." echo $latest_tag >| $GITHUB_WORKSPACE/branch/documentation/site/layouts/shortcodes/latestVersion.html + cat $GITHUB_WORKSPACE/branch/documentation/site/layouts/shortcodes/latestVersion.html echo "Building documentation for latest minor version..." hugo -s site -d "$GITHUB_WORKSPACE/WORK" -b https://oracle.github.io/weblogic-kubernetes-operator echo "Copying static files into place..." diff --git a/documentation/site/content/managing-domains/accessing-the-domain/weblogic-admin-console.md b/documentation/site/content/managing-domains/accessing-the-domain/weblogic-admin-console.md index 47ab8c66d1b..f6351deee5d 100644 --- a/documentation/site/content/managing-domains/accessing-the-domain/weblogic-admin-console.md +++ b/documentation/site/content/managing-domains/accessing-the-domain/weblogic-admin-console.md @@ -75,7 +75,7 @@ The following example sets up an ingress path routing rule to access a WebLogic `$ export LB_PORT=$(kubectl -n traefik get service traefik-operator -o jsonpath='{.spec.ports[?(@.name=="web")].nodePort}')` -If you have an [FMW Infrastructure]({{< relref "/managing-fmw-domains.md" >}}) domain, then you can add an ingress path routing rule for the PathPrefix `/em` and access Fusion Middleware Control (Enterprise Manager) using the following URL: +If you have an [FMW Infrastructure]({{< relref "/managing-domains/managing-fmw-domains.md" >}}) domain, then you can add an ingress path routing rule for the PathPrefix `/em` and access Fusion Middleware Control (Enterprise Manager) using the following URL: ``` http://${HOSTNAME}:${LB_PORT}/em @@ -152,7 +152,7 @@ The following example sets up load balancer routing for access to the WebLogic S https://${HOSTNAME}:${SSLPORT}/console ``` - If you have an [FMW Infrastructure]({{< relref "/managing-fmw-domains.md" >}}) domain, then you can add an ingress path routing rule for the PathPrefix `/em` and access Fusion Middleware Control (Enterprise Manager) using the following URL: + If you have an [FMW Infrastructure]({{< relref "/managing-domains/managing-fmw-domains.md" >}}) domain, then you can add an ingress path routing rule for the PathPrefix `/em` and access Fusion Middleware Control (Enterprise Manager) using the following URL: ``` https://${HOSTNAME}:${SSLPORT}/em @@ -173,7 +173,7 @@ Use the following steps to configure a `NodePort` to access the WebLogic Server ``` The `adminserver-NodePort` is the port number of the Administration Server outside the Kubernetes cluster. - If you have an [FMW Infrastructure]({{< relref "/managing-fmw-domains.md" >}}) domain, then you can also access Fusion Middleware Control (Enterprise Manager) using the following URL: + If you have an [FMW Infrastructure]({{< relref "/managing-domains/managing-fmw-domains.md" >}}) domain, then you can also access Fusion Middleware Control (Enterprise Manager) using the following URL: ``` http://hostname:adminserver-NodePort/em @@ -206,7 +206,7 @@ A Kubernetes port forward command is convenient for development use cases but is * `${LOCAL_PORT}` is the local port specified on the `kubectl port-forward` command line. - If you have an [FMW Infrastructure]({{< relref "/managing-fmw-domains.md" >}}) domain, then you can also access Fusion Middleware Control (Enterprise Manager) using the following URL: + If you have an [FMW Infrastructure]({{< relref "/managing-domains/managing-fmw-domains.md" >}}) domain, then you can also access Fusion Middleware Control (Enterprise Manager) using the following URL: ``` http://${HOSTNAME}:${LOCAL_PORT}/em diff --git a/documentation/site/content/managing-domains/managing-fmw-domains/_index.md b/documentation/site/content/managing-domains/managing-fmw-domains.md similarity index 100% rename from documentation/site/content/managing-domains/managing-fmw-domains/_index.md rename to documentation/site/content/managing-domains/managing-fmw-domains.md From 74bcd4d8334dce8520609830c522cbd23170752e Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Wed, 21 Aug 2024 22:19:40 +0000 Subject: [PATCH 136/356] Fix the CRON settings to match with job name for the nightly to run --- Jenkinsfile.oke | 1 + Jenkinsfile.okeext | 1 + Jenkinsfile.podman.upgrade | 3 ++- .../src/test/resources/bash-scripts/install-istio.sh | 4 ++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index bcc387c17e4..058d44f747a 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -102,6 +102,7 @@ pipeline { choice(name: 'ISTIO_VERSION', description: 'Istio version', choices: [ + '1.23.0', '1.17.2', '1.16.1', '1.13.2', diff --git a/Jenkinsfile.okeext b/Jenkinsfile.okeext index 83ee9ecdf38..67f68fe018b 100644 --- a/Jenkinsfile.okeext +++ b/Jenkinsfile.okeext @@ -80,6 +80,7 @@ pipeline { choice(name: 'ISTIO_VERSION', description: 'Istio version', choices: [ + '1.23.0', '1.17.2', '1.16.1', '1.13.2', diff --git a/Jenkinsfile.podman.upgrade b/Jenkinsfile.podman.upgrade index 2160dab31d7..308a8c9cf7c 100644 --- a/Jenkinsfile.podman.upgrade +++ b/Jenkinsfile.podman.upgrade @@ -30,7 +30,7 @@ pipeline { triggers { // timer trigger for "nightly build" - parameterizedCron(env.JOB_NAME == 'wko-kind-main-nightly-podman-upgrade' ? + parameterizedCron(env.JOB_NAME == 'wko-kind-release42-nightly-podman-upgrade' ? CRON_SETTINGS : '') } @@ -117,6 +117,7 @@ pipeline { choice(name: 'ISTIO_VERSION', description: 'Istio version', choices: [ + '1.23.0', '1.17.2', '1.16.1', '1.13.2', diff --git a/integration-tests/src/test/resources/bash-scripts/install-istio.sh b/integration-tests/src/test/resources/bash-scripts/install-istio.sh index 506811c8bb8..fe502b0ffd9 100755 --- a/integration-tests/src/test/resources/bash-scripts/install-istio.sh +++ b/integration-tests/src/test/resources/bash-scripts/install-istio.sh @@ -5,7 +5,7 @@ # Description: # # This script install a given version of istio using Helm v3.x -# Default istio version is 1.13.2 +# Default istio version is 1.23.0 # https://istio.io/docs/setup/install/istioctl/ # https://istio.io/latest/docs/setup/install/standalone-operator/ # https://github.com/istio/istio/releases @@ -56,7 +56,7 @@ ${KUBERNETES_CLI} create namespace istio-system } # MAIN -version=${1:-1.13.2} +version=${1:-1.23.0} workdir=${2:-`pwd`} wko_tenancy=${3:-devweblogic} arch=${4:-linux-amd64} From 19179cdd00d29612c6b8a053a101515fb9fd9dd7 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 21 Aug 2024 22:21:46 +0000 Subject: [PATCH 137/356] Merge branch 'rm/deploy-wrc-extension' into 'main' Add WRC extension steps See merge request weblogic-cloud/weblogic-kubernetes-operator!4787 (cherry picked from commit 673fddca1d6dcf1cf7bdb1c17499ad42014baade) 87c2d5f7 Add WRC extension steps --- .../accessing-the-domain/remote-admin-console.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md b/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md index 6982efa7b52..dafe6c2a2d2 100644 --- a/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md +++ b/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md @@ -45,6 +45,22 @@ To set up access to WebLogic Server domains running in Kubernetes using the Remo **NOTE**: These instructions assume that you are installing and running the Remote Console externally to your Kubernetes cluster. +1. For [additional functionality](https://oracle.github.io/weblogic-remote-console/setup/console/#ext), incorporate and deploy the WebLogic Remote Console extension in your 12.2.1.4 and 14.1.1 domains. + + a. From [https://github.com/oracle/weblogic-remote-console/releases](https://github.com/oracle/weblogic-remote-console/releases), download the Remote Console extension WAR file, [console-rest-ext-[version].war](https://github.com/oracle/weblogic-remote-console/releases/download/v2.4.10/console-rest-ext-9.0.war). + + b. Using the WebLogic Deploy Tooling (WDT) Archive Helper Tool, modify the WDT application archive to include the Remote Console Extension downloaded in the previous step. For example: + + ``` + /Directory to WDT/weblogic-deploy/bin/archiveHelper.sh add weblogicRemoteConsoleExtension archive_file=/Directory to WDT application archive/archive.zip -source=/Directory to Remote Console Extension/console-rest-ext[version].war + ``` + + For more information, see the [Archive Helper Tool](https://oracle.github.io/weblogic-deploy-tooling/userguide/tools/archive_helper/) documentation. + + c. With the [WebLogic Image Tool](https://oracle.github.io/weblogic-image-tool/), create an auxiliary image and include the archive modified in the previous step. + + d. Provision or update the domain using the new auxiliary image. + 1. When you first launch the Remote Console, it will prompt you with a login dialog for a WebLogic Server Administration Server URL. To give the Remote Console access to an Administration Server running in Kubernetes, you can: * Use an [Administration Server `NodePort`](#use-an-administration-server-nodeport). From 4624720622390845430337f65afc492f69fcf925 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 22 Aug 2024 20:27:23 +0000 Subject: [PATCH 138/356] Merge branch 'mii-auxiliary-image' into 'main' Modify AKS mii sample to use auxiliary image. See merge request weblogic-cloud/weblogic-kubernetes-operator!4788 (cherry picked from commit 2c6a2d5d7cfb2b0b765f40e95426573c2840251d) b7c85964 modify mii sample to use auxiliary image. 05784ac9 On branch edburns-msft-o-483 --- .../azure-kubernetes-service/domain-on-pv.md | 75 +------ .../includes/auxiliary-image-directory.txt | 4 + .../run-mii-to-create-auxiliary-image.txt | 40 ++++ .../includes/sign-in-azure.txt | 26 +++ .../model-in-image.md | 154 +++++++-------- .../azure-csi-storageaccount-template.yaml | 13 -- .../azure-file-pvc-template.yaml | 14 -- .../create-domain-on-aks-mii-generate-yaml.sh | 186 ++++++++++++++++++ .../loadbalancer-template.yaml | 19 -- .../model-in-image/admin-lb.yaml | 19 -- .../model-in-image/cluster-lb.yaml | 19 -- .../helm-sa-cluster-admin-role.yaml | 15 -- 12 files changed, 328 insertions(+), 256 deletions(-) create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt create mode 100644 documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-csi-storageaccount-template.yaml delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pvc-template.yaml create mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/loadbalancer-template.yaml delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml delete mode 100644 kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/helm-sa-cluster-admin-role.yaml diff --git a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md index 5e1ff1c7e91..e45405c7cc6 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md +++ b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md @@ -57,32 +57,7 @@ export ACR_NAME="${NAME_PREFIX}acr${TIMESTAMP}" {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt" >}} -##### Sign in with Azure CLI - -The steps in this section show you how to sign in to the Azure CLI. - -1. Open a Bash shell. - -1. Sign out and delete some authentication files to remove any lingering credentials. - - ```shell - $ az logout - $ rm ~/.azure/accessTokens.json - $ rm ~/.azure/azureProfile.json - ``` - -1. Sign in to your Azure CLI. - - ```shell - $ az login - ``` - -1. Set the subscription ID. Be sure to replace the placeholder with the appropriate value. - - ```shell - $ export SUBSCRIPTION_ID=$(az account show --query id --output tsv) - $ az account set -s $SUBSCRIPTION_ID - ``` +{{< readfile file="/samples/azure-kubernetes-service/includes/sign-in-azure.txt" >}} {{% notice info %}} The following sections of the sample instructions will guide you, step-by-step, through the process of setting up a WebLogic cluster on AKS - remaining as close as possible to a native Kubernetes experience. This lets you understand and customize each step. If you wish to have a more automated experience that abstracts some lower level details, you can skip to the [Automation](#automation) section. {{% /notice %}} @@ -138,10 +113,7 @@ This sample requires [Domain creation images]({{< relref "/managing-domains/doma ##### Image creation - Introduction -The goal of image creation is to demonstrate using the WebLogic Image Tool to create an image tagged as `wdt-domain-image:WLS-v1` from files that you will stage to `${WDT_MODEL_FILES_PATH}/WLS-v1`. - - - The directory where the WebLogic Deploy Tooling software is installed (also known as WDT Home), expected in an image’s `/auxiliary/weblogic-deploy` directory, by default. - - WDT model YAML (model), WDT variable (property), and WDT archive ZIP (archive) files, expected in directory `/auxiliary/models`, by default. +{{< readfile file="/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt" >}} ##### Understanding your first archive @@ -170,46 +142,11 @@ An image can contain multiple properties files, archive ZIP files, and model YAM ##### Creating the image with WIT -At this point, you have all of the files needed for `image wdt-domain-image:WLS-v1` staged; they include: - - - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/model.10.yaml` - - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/model.10.properties` - - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/archive.zip` - -Now, you use the Image Tool to create an image named `wdt-domain-image:WLS-v1`. You’ve already set up this tool during the prerequisite steps. +{{< readfile file="/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt" >}} -Run the following commands to create the image and verify that it worked. Note that `amagetool.sh` is not supported on macOS with Apple Silicon. See [Troubleshooting - exec format error]({{< relref "/samples/azure-kubernetes-service/troubleshooting#exec-weblogic-operatorscriptsintrospectdomainsh-exec-format-error" >}}). - -```shell -$ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh createAuxImage \ - --tag wdt-domain-image:WLS-v1 \ - --wdtModel ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.yaml \ - --wdtVariables ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.properties \ - --wdtArchive ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip -``` - -This command runs the WebLogic Image Tool to create the domain creation image and does the following: - - - Builds the final container image as a layer on a small `busybox` base image. - - Copies the WDT ZIP file that's referenced in the WIT cache into the image. - - Note that you cached WDT in WIT using the keyword `latest` when you set up the cache during the sample prerequisites steps. - - This lets WIT implicitly assume it's the desired WDT version and removes the need to pass a `-wdtVersion` flag. - - Copies the specified WDT model, properties, and application archives to image location `/auxiliary/models`. - -When the command succeeds, it should end with output like the following: - -``` -[INFO ] Build successful. Build time=70s. Image tag=wdt-domain-image:WLS-v1 -``` - -Verify the image is available in the local Docker server with the following command. - -```shell -$ docker images | grep WLS-v1 -``` -``` -wdt-domain-image WLS-v1 012d3bfa3536 5 days ago 1.13GB -``` +{{% notice note %}} +The `imagetool.sh` is not supported on macOS with Apple Silicon. See [Troubleshooting - exec format error]({{< relref "/samples/azure-kubernetes-service/troubleshooting#exec-weblogic-operatorscriptsintrospectdomainsh-exec-format-error" >}}). +{{% /notice %}} {{% notice note %}} You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled, see [Troubleshooting - WebLogic Image Tool failure]({{< relref "/samples/azure-kubernetes-service/troubleshooting#weblogic-image-tool-failure" >}}). diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt new file mode 100644 index 00000000000..ecd0555d5a0 --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt @@ -0,0 +1,4 @@ +The goal of image creation is to demonstrate using the WebLogic Image Tool to create an image tagged as `wdt-domain-image:WLS-v1` from files that you will stage to `${WDT_MODEL_FILES_PATH}/WLS-v1/`. + + - The directory where the WebLogic Deploy Tooling software is installed (also known as WDT Home) is expected in an image’s `/auxiliary/weblogic-deploy` directory. + - WDT model YAML (model), WDT variable (property), and WDT archive ZIP (archive) files are expected in directory `/auxiliary/models`. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt new file mode 100644 index 00000000000..e30bb09d18f --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt @@ -0,0 +1,40 @@ +At this point, you have all of the files needed for `image wdt-domain-image:WLS-v1` staged; they include: + + - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/model.10.yaml` + - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/model.10.properties` + - `/tmp/sample/wdt-artifacts/wdt-model-files/WLS-v1/archive.zip` + +Now, you use the Image Tool to create an image named `wdt-domain-image:WLS-v1`. You’ve already set up this tool during the prerequisite steps. + +Run the following commands to create the image and verify that it worked. + +```shell +$ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh createAuxImage \ + --tag wdt-domain-image:WLS-v1 \ + --wdtModel ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.yaml \ + --wdtVariables ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.properties \ + --wdtArchive ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip +``` + +This command runs the WebLogic Image Tool to create the domain creation image and does the following: + + - Builds the final container image as a layer on a small `busybox` base image. + - Copies the WDT ZIP file that's referenced in the WIT cache into the image. + - Note that you cached WDT in WIT using the keyword `latest` when you set up the cache during the sample prerequisites steps. + - This lets WIT implicitly assume it's the desired WDT version and removes the need to pass a `-wdtVersion` flag. + - Copies the specified WDT model, properties, and application archives to image location `/auxiliary/models`. + +When the command succeeds, it should end with output like the following: + +``` +[INFO ] Build successful. Build time=70s. Image tag=wdt-domain-image:WLS-v1 +``` + +Verify the image is available in the local Docker server with the following command. + +```shell +$ docker images | grep WLS-v1 +``` +``` +wdt-domain-image WLS-v1 012d3bfa3536 5 days ago 1.13GB +``` \ No newline at end of file diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt new file mode 100644 index 00000000000..09dc349c697 --- /dev/null +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt @@ -0,0 +1,26 @@ +##### Sign in with Azure CLI + +The steps in this section show you how to sign in to the Azure CLI. + +1. Open a Bash shell. + +1. Sign out and delete some authentication files to remove any lingering credentials. + + ```shell + $ az logout + $ rm ~/.azure/accessTokens.json + $ rm ~/.azure/azureProfile.json + ``` + +1. Sign in to your Azure CLI. + + ```shell + $ az login + ``` + +1. Set the subscription ID. Be sure to replace the placeholder with the appropriate value. + + ```shell + $ export SUBSCRIPTION_ID="" + $ az account set -s $SUBSCRIPTION_ID + ``` \ No newline at end of file diff --git a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md index d8f0bd1c5cc..37399013a65 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md +++ b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md @@ -22,7 +22,7 @@ This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://o {{< readfile file="/samples/azure-kubernetes-service/includes/prerequisites-02.txt" >}} -#### Prepare parameters +##### Prepare parameters Set parameters. @@ -31,6 +31,9 @@ Set parameters. export ORACLE_SSO_EMAIL= export ORACLE_SSO_PASSWORD="" +export BASE_DIR=~ +export NAME_PREFIX=wls + # Used to generate resource names. export TIMESTAMP=`date +%s` export ACR_NAME="acr${TIMESTAMP}" @@ -38,15 +41,16 @@ export AKS_CLUSTER_NAME="aks${TIMESTAMP}" export AKS_PERS_RESOURCE_GROUP="resourcegroup${TIMESTAMP}" export AKS_PERS_LOCATION=eastus +export SECRET_NAME_DOCKER="${NAME_PREFIX}regcred" export WEBLOGIC_USERNAME=weblogic export WEBLOGIC_PASSWORD=Secret123456 export WEBLOGIC_WDT_PASSWORD=Secret123456 - -export BASE_DIR=~ ``` {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt" >}} +{{< readfile file="/samples/azure-kubernetes-service/includes/sign-in-azure.txt" >}} + {{< readfile file="/samples/azure-kubernetes-service/includes/download-samples-zip.txt" >}} {{< readfile file="/samples/azure-kubernetes-service/includes/create-resource-group.txt" >}} @@ -161,6 +165,7 @@ If you have an image built with domain models following [Model in Image]({{< rel - Copy the sample to a new directory; for example, use the directory `/tmp/mii-sample`. In the directory name, `mii` is short for "model in image". Model in image is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). ```shell + $ rm /tmp/mii-sample -f -r $ mkdir /tmp/mii-sample ``` @@ -181,28 +186,11 @@ If you have an image built with domain models following [Model in Image]({{< rel ##### Image creation - Introduction -The goal of image creation is to demonstrate using the WebLogic Image Tool to create an image tagged as `model-in-image:WLS-v1` from files that you will stage to `/tmp/mii-sample/wdt-model-files/WLS-v1/`. -The staged files will contain a web application in a WDT archive, and WDT model configuration for a WebLogic Administration Server called `admin-server` and a WebLogic cluster called `cluster-1`. - -A "Model in Image" image contains the following elements: -* A WebLogic Server installation (including operating system and JDK) and a WebLogic Deploy Tooling installation in its `/u01/wdt/weblogic-deploy` directory. -* If you have WDT model archive files, then the image must also contain these files in its `/u01/wdt/models` directory. -* If you have WDT model YAML file and properties files, then they go in in the same `/u01/wdt/models` directory. If you do not specify a WDT model YAML file in your `/u01/wdt/models` directory, then the model YAML file must be supplied dynamically using a Kubernetes `ConfigMap` that is referenced by your Domain `spec.model.configMap` field. - -We provide an example of using a model `ConfigMap` later in this sample. - -The following sections contain the steps for creating the image `model-in-image:WLS-v1`. +{{< readfile file="/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt" >}} ##### Understanding your first archive -The sample includes a predefined archive directory in `/tmp/mii-sample/archives/archive-v1` that you will use to create an archive ZIP file for the image. - -The archive top directory, named `wlsdeploy`, contains a directory named `applications`, which includes an ‘exploded’ sample JSP web application in the directory, `myapp-v1`. Three useful aspects to remember about WDT archives are: - - A model image can contain multiple WDT archives. - - WDT archives can contain multiple applications, libraries, and other components. - - WDT archives have a [well defined directory structure](https://oracle.github.io/weblogic-deploy-tooling/concepts/archive/), which always has `wlsdeploy` as the top directory. - -The application displays important details about the WebLogic Server instance that it’s running on: namely its domain name, cluster name, and server name, as well as the names of any data sources that are targeted to the server. +See [Understanding your first archive]({{< relref "/samples/domains/model-in-image/auxiliary-image-creation#understand-your-first-archive" >}}). ##### Staging a ZIP file of the archive @@ -231,55 +219,11 @@ A Model in Image image can contain multiple properties files, archive ZIP files, ##### Creating the image with WIT -At this point, you have staged all of the files needed for the image `model-in-image:WLS-v1`; they include: - - - `/tmp/mii-sample/wdt-model-files/weblogic-deploy.zip` - - `/tmp/mii-sample/wdt-model-files/WLS-v1/model.10.yaml` - - `/tmp/mii-sample/wdt-model-files/WLS-v1/model.10.properties` - - `/tmp/mii-sample/wdt-model-files/WLS-v1/archive.zip` - -If you don’t see the `weblogic-deploy.zip` file, then you missed a step in the [prerequisites](#image-creation-prerequisites). - -Now, you use the Image Tool to create an image named `model-in-image:WLS-v1` with a `FROM` clause that references a base WebLogic image. You’ve already set up this tool during the prerequisite steps. - -Run the following commands to create the model image and verify that it worked: - -```shell -$ ${WDT_MODEL_FILES_PATH}/imagetool/bin/imagetool.sh update \ - --tag model-in-image:WLS-v1 \ - --fromImage container-registry.oracle.com/middleware/weblogic:12.2.1.4 \ - --wdtModel ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.yaml \ - --wdtVariables ${WDT_MODEL_FILES_PATH}/WLS-v1/model.10.properties \ - --wdtArchive ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip \ - --wdtModelOnly \ - --wdtDomainType WLS \ - --chown oracle:root -``` - -If you don’t see the `imagetool` directory, then you missed a step in the prerequisites. - -The preceding command runs the WebLogic Image Tool in its Model in Image mode, and does the following: - - - Builds the final image as a layer on the `container-registry.oracle.com/middleware/weblogic:12.2.1.4` base image. - - Copies the WDT ZIP file that’s referenced in the WIT cache into the image. - - Note that you cached WDT in WIT using the keyword `latest` when you set up the cache during the sample prerequisites steps. - - This lets WIT implicitly assume it’s the desired WDT version and removes the need to pass a `-wdtVersion` flag. - - Copies the specified WDT model, properties, and application archives to image location `/u01/wdt/models`. - -When the command succeeds, you should see output like the following: - -``` -[INFO ] Build successful. Build time=36s. Image tag=model-in-image:WLS-v1 -``` - -Verify the image is available in the local Docker server with the following command. +{{< readfile file="/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt" >}} -```shell -$ docker images | grep WLS-v1 -``` -``` -model-in-image WLS-v1 012d3bfa3536 5 days ago 1.13GB -``` +{{% notice note %}} +The `imagetool.sh` is not supported on macOS with Apple Silicon. See [Troubleshooting - exec format error]({{< relref "/samples/azure-kubernetes-service/troubleshooting#exec-weblogic-operatorscriptsintrospectdomainsh-exec-format-error" >}}). +{{% /notice %}} {{% notice note %}} You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled, see [Troubleshooting - WebLogic Image Tool failure]({{< relref "/samples/azure-kubernetes-service/troubleshooting#weblogic-image-tool-failure" >}}). @@ -292,13 +236,13 @@ You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled Ensure Docker is running on your local machine. Run the following commands to tag and push the image to your ACR. ```shell -$ docker tag model-in-image:WLS-v1 $LOGIN_SERVER/model-in-image-aks:1.0 +$ docker tag wdt-domain-image:WLS-v1 $LOGIN_SERVER/mii-aks-auxiliary-image:1.0 ``` ```shell -$ docker push $LOGIN_SERVER/model-in-image-aks:1.0 +$ docker push $LOGIN_SERVER/mii-aks-auxiliary-image:1.0 ``` ``` -The push refers to repository [contosorgresourcegroup1610068510.azurecr.io/model-in-image-aks] +The push refers to repository [contosorgresourcegroup1610068510.azurecr.io/mii-aks-auxiliary-image] 1.0: digest: sha256:208217afe336053e4c524caeea1a415ccc9cc73b206ee58175d0acc5a3eeddd9 size: 2415 ``` @@ -308,6 +252,11 @@ If you see an error that seems related to you not being an **Owner on this subsc #### Create WebLogic domain + - [Namespace](#namespace) + - [Kubernetes Secrets for WebLogic image](#kubernetes-secrets-for-weblogic-image) + - [Kubernetes Secrets for WebLogic](#kubernetes-secrets-for-weblogic) + - [Domain resource](##domain-resource) + In this section, you will deploy the new image to the namespace `sample-domain1-ns`, including the following steps: - Create a namespace for the WebLogic domain. @@ -334,6 +283,23 @@ Label the domain namespace so that the operator can autodetect and create WebLog $ kubectl label namespace sample-domain1-ns weblogic-operator=enabled ``` +##### Kubernetes Secrets for WebLogic image + +You will use the `kubernetes/samples/scripts/create-kubernetes-secrets/create-docker-credentials-secret.sh` script to create the Docker credentials as a Kubernetes secret to pull image from OCR. Please run: + +``` shell +$ $BASE_DIR/sample-scripts/create-kubernetes-secrets/create-docker-credentials-secret.sh \ + -n sample-domain1-ns \ + -s ${SECRET_NAME_DOCKER} \ + -e ${ORACLE_SSO_EMAIL} \ + -p ${ORACLE_SSO_PASSWORD} \ + -u ${ORACLE_SSO_EMAIL} +``` +``` +secret/wlsregcred created +The secret wlsregcred has been successfully created in the sample-domain1-ns namespace. +``` + ##### Kubernetes Secrets for WebLogic First, create the secrets needed by the WLS type model domain. For more on secrets in the context of running domains, see [Prepare to run a domain]({{< relref "/managing-domains/prepare" >}}). In this case, you have two secrets. @@ -391,32 +357,44 @@ $ kubectl -n sample-domain1-ns label secret \ - To make it obvious which secrets belong to which domains. - To make it easier to clean up a domain. Typical cleanup scripts use the `weblogic.domainUID` label as a convenience for finding all resources associated with a domain. +Now, you can verify the secrets with command: + +```shell +kubectl get secrets -n sample-domain1-ns +``` + +The output looks similar to the following content. + +```txt +NAME TYPE DATA AGE +sample-domain1-runtime-encryption-secret Opaque 1 19s +sample-domain1-weblogic-credentials Opaque 2 28s +wlsregcred kubernetes.io/dockerconfigjson 1 47s +``` + ##### Domain resource Now, you create a domain YAML file. Think of the domain YAML file as the way to configure some aspects of your WebLogic domain using Kubernetes. The operator uses the Kubernetes "custom resource" feature to define a Kubernetes resource type called `Domain`. For more on the `Domain` Kubernetes resource, see [Domain Resource]({{< relref "/managing-domains/domain-resource" >}}). For more on custom resources see [the Kubernetes documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). -We provide a sample file at `$BASE_DIR/sample-scripts/create-weblogic-domain/model-in-image/domain-resources/WLS-LEGACY/mii-initial-d1-WLS-LEGACY-v1.yaml`, copy it to a file called `/tmp/mii-sample/mii-initial.yaml`. +We provide a script at `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh` to generate domain resource description. + +Run the following command to generate resource files. ```shell -$ cp $BASE_DIR/sample-scripts/create-weblogic-domain/model-in-image/domain-resources/WLS-LEGACY/mii-initial-d1-WLS-LEGACY-v1.yaml /tmp/mii-sample/mii-initial.yaml +export Domain_Creation_Image_tag="$LOGIN_SERVER/mii-aks-auxiliary-image:1.0" ``` -Print the image path. Copy the output to your clipboard and paste it to value of `spec.image` in `/tmp/mii-sample/mii-initial.yaml`. - ```shell -echo $LOGIN_SERVER/model-in-image-aks:1.0 +$ cd $BASE_DIR +$ bash $BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh ``` -Modify the Domain YAML with your values. - -| Name in YAML file | Example value | Notes | -|-------------------|---------------|-------| -|`spec.image`|`$LOGIN_SERVER/model-in-image-aks:1.0`|Must be the same as the value to which you pushed the image to by running the command `docker push $LOGIN_SERVER/model-in-image-aks:1.0`.| +After running above commands, you will get three files: `mii-initial.yaml`, `admin-lb.yaml` and `cluster-lb.yaml`. Run the following command to create the domain custom resource: ```shell -$ kubectl apply -f /tmp/mii-sample/mii-initial.yaml +$ kubectl apply -f mii-initial.yaml ``` Successful output will look like: @@ -477,7 +455,7 @@ If the system does not reach this state, troubleshoot and resolve the problem be Create the Azure public standard load balancer to access the WebLogic Server Administration Console and applications deployed in the cluster. -Use the configuration file in `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml` to create a load balancer service for the Administration Server. If you are choosing not to use the predefined YAML file and instead created a new one with customized values, then substitute the following content with you domain values. +Use the configuration file in `admin-lb.yaml` to create a load balancer service for the Administration Server. If you are choosing not to use the predefined YAML file and instead created a new one with customized values, then substitute the following content with you domain values. {{%expand "Click here to view YAML content." %}} ```yaml @@ -500,7 +478,7 @@ spec: ``` {{% /expand %}} -Use the configuration file in `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml` to create a load balancer service for the managed servers. If you are choosing not to use the predefined YAML file and instead created new one with customized values, then substitute the following content with you domain values. +Use the configuration file in `cluster-lb.yaml` to create a load balancer service for the managed servers. If you are choosing not to use the predefined YAML file and instead created new one with customized values, then substitute the following content with you domain values. {{%expand "Click here to view YAML content." %}} ```yaml @@ -527,13 +505,13 @@ spec: Create the load balancer services using the following command: ```shell -$ kubectl apply -f $BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/admin-lb.yaml +$ kubectl apply -f admin-lb.yaml ``` ``` service/sample-domain1-admin-server-external-lb created ``` ```shell -$ kubectl apply -f $BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/model-in-image/cluster-lb.yaml +$ kubectl apply -f cluster-lb.yaml ``` ``` service/sample-domain1-cluster-1-external-lb created diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-csi-storageaccount-template.yaml b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-csi-storageaccount-template.yaml deleted file mode 100644 index 2547f2313c7..00000000000 --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-csi-storageaccount-template.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: %STORAGE_CLASS_NAME% -provisioner: file.csi.azure.com -allowVolumeExpansion: true -parameters: - protocol: nfs -mountOptions: - - nconnect=4 diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pvc-template.yaml b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pvc-template.yaml deleted file mode 100644 index cd5aa6866a3..00000000000 --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pvc-template.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: %PERSISTENT_VOLUME_CLAIM_NAME% -spec: - accessModes: - - ReadWriteMany - storageClassName: %STORAGE_CLASS_NAME% - resources: - requests: - storage: 100Gi diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh new file mode 100644 index 00000000000..26748997b92 --- /dev/null +++ b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh @@ -0,0 +1,186 @@ +cat >mii-initial.yaml <admin-lb.yaml <cluster-lb.yaml < Date: Mon, 26 Aug 2024 19:03:26 +0000 Subject: [PATCH 139/356] disable upgrade tests from 338 --- .../java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java | 3 ++- .../oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java index 9bed49957e0..d7a310950fa 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorFmwUpgrade.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -194,7 +195,7 @@ public void tearDown() { /** * Operator upgrade from 3.3.8 to current with a FMW Domain. */ - @Test + @Disabled @DisplayName("Upgrade Operator from 3.3.8 to current") void testOperatorFmwUpgradeFrom338ToCurrent() { installAndUpgradeOperator("3.3.8", "v8", DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java index 62cae38d43e..41f9948a783 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java @@ -21,6 +21,7 @@ import oracle.weblogic.kubernetes.logging.LoggingFacade; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -142,7 +143,7 @@ void testOperatorWlsIstioDomainUpgradeFrom344ToCurrent() { /** * Upgrade from Operator v3.3.8 to current with Istio enabled domain. */ - @Test + @Disabled @DisplayName("Upgrade 3.3.8 Istio Domain(v8) with Istio to current") void testOperatorWlsIstioDomainUpgradeFrom338ToCurrent() { logger.info("Starting test to upgrade Istio Image Domain with Istio with v8 schema to current"); From 710059fde93cd73e0cae62709c4a679894780845 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 26 Aug 2024 20:46:36 +0000 Subject: [PATCH 140/356] Merge branch '14120-ports-related-fix' into 'main' Add support for additional new topology level attributes to control... See merge request weblogic-cloud/weblogic-kubernetes-operator!4792 (cherry picked from commit 02c7a6de3746f2598a1c364b78f42335eb05c84d) b756bc63 Add support for additional new topology level attributes to control... eb717698 Fix internal-t3 for 14120 when global listen port is enabled e23d88cf fix ssl global logic b0c409a7 Fix various default port logic for 14.1.2 global settings --- .../resources/scripts/introspectDomain.py | 17 +++++-- .../resources/scripts/model_wdt_mii_filter.py | 46 +++++++++++++++++-- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/operator/src/main/resources/scripts/introspectDomain.py b/operator/src/main/resources/scripts/introspectDomain.py index d1e20d0936a..3412c8f585e 100644 --- a/operator/src/main/resources/scripts/introspectDomain.py +++ b/operator/src/main/resources/scripts/introspectDomain.py @@ -2075,8 +2075,8 @@ def isSecureModeEnabledForDomain(domain): if attributes['SecureModeEnabled']: secureModeEnabled = True else: - secureModeEnabled = domain.isProductionModeEnabled() and not LegalHelper.versionEarlierThan(domain.getDomainVersion(), "14.1.2.0") - + secureModeEnabled = domain.isProductionModeEnabled() and not LegalHelper.versionEarlierThan(domain.getDomainVersion(), "14.1.2.0") \ + and domain.isAdministrationPortEnabled() return secureModeEnabled def isAdministrationPortEnabledForDomain(domain): @@ -2133,7 +2133,9 @@ def isListenPortEnabledForServer(server, domain, is_server_template=False): cd('/Server') cd(server.getName()) if not isSet('ListenPortEnabled') and isSecureModeEnabledForDomain(domain): - enabled = False + enabled = False + if not LegalHelper.versionEarlierThan(domain.getDomainVersion(), "14.1.2.0") and domain.isListenPortEnabled(): + enabled = True return enabled def isSSLListenPortEnabled(ssl, domain): @@ -2161,6 +2163,7 @@ def getSSLPortIfEnabled(server, domain, is_server_template=True): """ ssl = None ssl_listen_port = None + try: # this can throw if SSL mbean not there ssl = server.getSSL() @@ -2178,6 +2181,14 @@ def getSSLPortIfEnabled(server, domain, is_server_template=True): ssl_listen_port = getRealSSLListenPort(server, ssl.getListenPort()) elif ssl is None and isSecureModeEnabledForDomain(domain): ssl_listen_port = "7002" + + # Check override for 14.1.2.x + if not LegalHelper.versionEarlierThan(domain.getDomainVersion(), "14.1.2.0"): + if ssl is None and domain.isSSLEnabled(): + ssl_listen_port = 7002 + elif ssl is None and not domain.isSSLEnabled(): + ssl_listen_port = None + return ssl_listen_port def get_server_template_listening_ports_from_configxml(config_xml): diff --git a/operator/src/main/resources/scripts/model_wdt_mii_filter.py b/operator/src/main/resources/scripts/model_wdt_mii_filter.py index c2b651084e3..7feaa85736a 100644 --- a/operator/src/main/resources/scripts/model_wdt_mii_filter.py +++ b/operator/src/main/resources/scripts/model_wdt_mii_filter.py @@ -550,8 +550,33 @@ def _get_ssl_listen_port(server): ssl_listen_port = "7002" elif ssl is None and isSecureModeEnabledForDomain(model): ssl_listen_port = "7002" + + # Check overrride for 14.1.2.x + if not env.wlsVersionEarlierThan("14.1.2.0") and not isGlobalSSLEnabled(): + return None return ssl_listen_port +def isGlobalSSLEnabled(model): + result=False + if 'topology' in model: + if 'SSLEnabled' in model['topology']: + val = model['topology']['SSLEnabled'] + if isinstance(val, str) or isinstance(val, unicode): + result = Boolean.valueOf(val) + else: + result = val + return result + +def isGlobalListenPortEnabled(model): + result=False + if 'topology' in model: + if 'ListenPortEnabled' in model['topology']: + val = model['topology']['ListenPortEnabled'] + if isinstance(val, str) or isinstance(val, unicode): + result = Boolean.valueOf(val) + else: + result = val + return result def addAdminChannelPortForwardNetworkAccessPoints(server): admin_channel_port_forwarding_enabled = env.getEnvOrDef("ADMIN_CHANNEL_PORT_FORWARDING_ENABLED", "true") @@ -579,8 +604,14 @@ def addAdminChannelPortForwardNetworkAccessPoints(server): _writeAdminChannelPortForwardNAP(name='internal-admin', server=server, listen_port=getAdministrationPort(server, model['topology']), protocol='admin') elif index == 0: - if not secure_mode and is_listenport_enabled(server): - _writeAdminChannelPortForwardNAP(name='internal-t3', server=server, listen_port=admin_server_port, protocol='t3') + if not env.wlsVersionEarlierThan("14.1.2.0"): + if not secure_mode and is_listenport_enabled(server): + _writeAdminChannelPortForwardNAP(name='internal-t3', server=server, listen_port=admin_server_port, protocol='t3') + elif secure_mode and (is_listenport_enabled(server) or isGlobalListenPortEnabled(model)): + _writeAdminChannelPortForwardNAP(name='internal-t3', server=server, listen_port=admin_server_port, protocol='t3') + else: + if not secure_mode and is_listenport_enabled(server): + _writeAdminChannelPortForwardNAP(name='internal-t3', server=server, listen_port=admin_server_port, protocol='t3') ssl = getSSLOrNone(server) ssl_listen_port = None @@ -590,20 +621,27 @@ def addAdminChannelPortForwardNetworkAccessPoints(server): ssl_listen_port = "7002" elif ssl is None and secure_mode: ssl_listen_port = "7002" + # Check override for 14.1.2.x + + if not env.wlsVersionEarlierThan("14.1.2.0") and ssl is None: + if isGlobalSSLEnabled(model): + ssl_listen_port = 7002 + else: + ssl_listen_port = None if ssl_listen_port is not None: _writeAdminChannelPortForwardNAP(name='internal-t3s', server=server, listen_port=ssl_listen_port, protocol='t3s') def is_listenport_enabled(server): + is_listen_port_enabled = True if 'ListenPortEnabled' in server: val = server['ListenPortEnabled'] if isinstance(val, str) or isinstance(val, unicode): is_listen_port_enabled = Boolean.valueOf(val) else: is_listen_port_enabled = val - else: - is_listen_port_enabled = True + return is_listen_port_enabled From 3b37a2433ec10aa91f57f4335ff2b79c809fda7a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 27 Aug 2024 19:16:14 +0000 Subject: [PATCH 141/356] Merge branch 'owls-121004' into 'main' Account for dot in RFC 1123 legal name conversion See merge request weblogic-cloud/weblogic-kubernetes-operator!4794 (cherry picked from commit 348c28df0a4e2715ecfe79c1b2353c112b9e0f99) 49a6022c Account for dot in RFC 1123 legal name conversion --- .../oracle/kubernetes/common/utils/CommonUtils.java | 2 +- .../kubernetes/common/utils/SchemaConversionUtils.java | 6 +----- .../kubernetes/operator/helpers/JobStepContext.java | 4 ++-- .../oracle/kubernetes/operator/helpers/LegalNames.java | 2 +- .../operator/helpers/DomainIntrospectorJobTest.java | 10 +++++----- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/common/src/main/java/oracle/kubernetes/common/utils/CommonUtils.java b/common/src/main/java/oracle/kubernetes/common/utils/CommonUtils.java index 508f216d8d8..48d04782563 100644 --- a/common/src/main/java/oracle/kubernetes/common/utils/CommonUtils.java +++ b/common/src/main/java/oracle/kubernetes/common/utils/CommonUtils.java @@ -52,7 +52,7 @@ private static boolean useLatestImage(String imageName) { * @return nearest DNS-1123 legal name */ public static String toDns1123LegalName(String value) { - return value.toLowerCase().replace('_', '-'); + return value.toLowerCase().replace('_', '-').replace('.', '-'); } /** diff --git a/common/src/main/java/oracle/kubernetes/common/utils/SchemaConversionUtils.java b/common/src/main/java/oracle/kubernetes/common/utils/SchemaConversionUtils.java index f3d85fc777b..cbdf4257ded 100644 --- a/common/src/main/java/oracle/kubernetes/common/utils/SchemaConversionUtils.java +++ b/common/src/main/java/oracle/kubernetes/common/utils/SchemaConversionUtils.java @@ -1054,10 +1054,6 @@ private void generateClusters(Map domain, List generateCluster(Map domainMeta, Map existingCluster) { Map cluster = new LinkedHashMap<>(); @@ -1065,7 +1061,7 @@ private Map generateCluster(Map domainMeta, cluster.put("kind", "Cluster"); Map clusterMeta = new LinkedHashMap<>(); clusterMeta.put("name", domainMeta.get("name") + "-" - + toDns1123LegalName((String) existingCluster.get(CLUSTER_NAME))); + + CommonUtils.toDns1123LegalName((String) existingCluster.get(CLUSTER_NAME))); clusterMeta.put(NAMESPACE, domainMeta.get(NAMESPACE)); Map labels = new LinkedHashMap<>(); labels.put("weblogic.createdByOperator", "true"); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 8c6361e442c..f4baad65253 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -757,10 +757,10 @@ protected V1Container createPrimaryContainer() { private String getVolumeName(String resourceName, String type) { try { - return getLegalVolumeName(resourceName, type); + return CommonUtils.toDns1123LegalName(getLegalVolumeName(resourceName, type)); } catch (Exception ex) { LOGGER.severe(MessageKeys.EXCEPTION, ex); - return resourceName; + return CommonUtils.toDns1123LegalName(resourceName); } } diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/LegalNames.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/LegalNames.java index bcf20ad740c..73189fc0c53 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/LegalNames.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/LegalNames.java @@ -141,7 +141,7 @@ private static String getExternalServiceNameSuffix() { * @return nearest DNS-1123 legal name */ public static String toDns1123LegalName(String value) { - return value.toLowerCase().replace('_', '-'); + return value.toLowerCase().replace('_', '-').replace('.', '-'); } public static boolean isDns1123LegalName(String value) { diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java index 8bf331d9638..0758bb8da0a 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java @@ -533,18 +533,18 @@ void whenJobCreatedWithoutInitializeDomainOnPVDefined_dontHaveSecretsVolumeAndMo @Test void whenJobCreatedWithInitializeDomainOnPVCreateDomainCMDefined_hasConfigMapVolumeAndMounts() { - V1ConfigMap cm = new V1ConfigMap().metadata(new V1ObjectMeta().name("initPvDomainCM").namespace(NS)); + V1ConfigMap cm = new V1ConfigMap().metadata(new V1ObjectMeta().name("initpvdomaincm").namespace(NS)); testSupport.defineResources(cm); - getConfigurator().withDomainCreationConfigMap("initPvDomainCM"); + getConfigurator().withDomainCreationConfigMap("initpvdomaincm"); List jobs = runStepsAndGetJobs(); V1Job job = jobs.get(0); assertThat(getJobPodSpec(job).getVolumes(), - hasItem(new V1Volume().name("initPvDomainCM-volume").configMap( - new V1ConfigMapVolumeSource().name("initPvDomainCM").defaultMode(365)))); + hasItem(new V1Volume().name("initpvdomaincm-volume").configMap( + new V1ConfigMapVolumeSource().name("initpvdomaincm").defaultMode(365)))); assertThat(getCreatedPodSpecContainers(jobs).get(0).getVolumeMounts(), - hasItem(new V1VolumeMount().name("initPvDomainCM-volume") + hasItem(new V1VolumeMount().name("initpvdomaincm-volume") .mountPath(WDTCONFIGMAP_MOUNT_PATH).readOnly(true))); } From 9c383400fe46cfbb8f24bc2e323ed769ee9c631f Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 27 Aug 2024 16:46:40 -0400 Subject: [PATCH 142/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 9cd2e8e0173..54a80fcc3b7 100644 --- a/pom.xml +++ b/pom.xml @@ -677,17 +677,17 @@ 3.3.1 3.7.1 3.6.0 - 3.4.0 + 3.4.1 10.17.0 1.0 3.4.0 - 3.2.4 + 3.2.5 2.0.0.0 1.3.3 2.0.1 2.0.1 1.0.39 - 1.8.0 + 1.9.0 1.5.1 1.4.0 1.17.1 @@ -706,7 +706,7 @@ 4.12.0 3.9.0 1.78.1 - 5.10.3 + 5.11.0 5.7.1 1.7.0 1.3.2 @@ -721,7 +721,7 @@ 2.2 2.11.0 10.0.3 - 2.0.15 + 2.0.16 1.5.6 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java From 4c86f1e268d43975c0d58bc1a6a57670fd0cddd9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 28 Aug 2024 11:36:56 -0400 Subject: [PATCH 143/356] WME 2.2.1 --- documentation/domains/Domain.json | 4 ++-- documentation/domains/Domain.md | 2 +- kubernetes/crd/domain-crd.yaml | 6 +++--- .../oracle/kubernetes/operator/KubernetesConstants.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index fbb59bbadf8..3528e55f5d6 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -879,8 +879,8 @@ "type": "object", "properties": { "image": { - "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0", - "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0", + "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1", + "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1", "type": "string" }, "imagePullPolicy": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 860a75555e6..35fa0d3c8d3 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -145,7 +145,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | Name | Type | Description | | --- | --- | --- | | `configuration` | Map | The configuration for the WebLogic Monitoring Exporter. If WebLogic Server instances are already running and have the monitoring exporter sidecar container, then changes to this field will be propagated to the exporter without requiring the restart of the WebLogic Server instances. | -| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 | +| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1 | | `imagePullPolicy` | string | The image pull policy for the WebLogic Monitoring Exporter sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. | | `port` | integer | The port exposed by the WebLogic Monitoring Exporter running in the sidecar container. Defaults to 8080. The port value must not conflict with a port used by any WebLogic Server instance, including the ports of built-in channels or network access points (NAPs). | | `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. | diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 239a5314da6..52a019ec048 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: b48d0f17054d017bd3d9df2d30929bab24882235a628b715f59092f288dd569e + weblogic.sha256: 71f7e932ff00465bdd41107cd2f1c5c8d16d658ba6787269b714574e62397b71 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -47,9 +47,9 @@ spec: appropriate. See https://github.com/oracle/weblogic-monitoring-exporter. properties: image: - default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 + default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1 description: The WebLogic Monitoring Exporter sidecar container - image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0 + image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1 type: string imagePullPolicy: description: The image pull policy for the WebLogic Monitoring diff --git a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java index f5b72cb2d0a..8668ad09c2d 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java @@ -8,7 +8,7 @@ /** Kubernetes constants. */ public interface KubernetesConstants { String DEFAULT_IMAGE = "container-registry.oracle.com/middleware/weblogic:12.2.1.4"; - String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.0"; + String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1"; String DEFAULT_FLUENTD_IMAGE = "fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2"; String EXPORTER_CONTAINER_NAME = "monitoring-exporter"; String LATEST_IMAGE_SUFFIX = ":latest"; From a3d8992640a941ede0307a55b9f17804524991a6 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 28 Aug 2024 13:56:12 -0400 Subject: [PATCH 144/356] Depenedency updates and CheckStyle compensating changes --- .../weblogic/kubernetes/ItIntrospectVersion.java | 2 +- .../weblogic/kubernetes/actions/TestActions.java | 2 +- .../weblogic/kubernetes/utils/CommonTestUtils.java | 2 +- .../weblogic/kubernetes/utils/MonitoringUtils.java | 2 +- ...ratedFilesOptionalFeaturesDisabledTestBase.java | 4 ++-- ...eratedFilesOptionalFeaturesEnabledTestBase.java | 4 ++-- .../CreateOperatorGeneratedFilesTestBase.java | 2 +- .../operator/utils/ParsedApacheSecurityYaml.java | 4 ++-- .../operator/utils/ParsedApacheYaml.java | 4 ++-- .../utils/ParsedCreateWeblogicDomainJobYaml.java | 4 ++-- .../utils/ParsedDeleteWeblogicDomainJobYaml.java | 4 ++-- .../utils/ParsedDomainCustomResourceYaml.java | 4 ++-- .../operator/utils/ParsedTraefikSecurityYaml.java | 4 ++-- .../operator/utils/ParsedTraefikYaml.java | 4 ++-- .../operator/utils/ParsedVoyagerIngressYaml.java | 4 ++-- .../utils/ParsedVoyagerOperatorSecurityYaml.java | 4 ++-- .../operator/utils/ParsedVoyagerOperatorYaml.java | 4 ++-- ...sedWeblogicDomainPersistentVolumeClaimYaml.java | 4 ++-- .../ParsedWeblogicDomainPersistentVolumeYaml.java | 4 ++-- .../utils/ParsedWeblogicOperatorSecurityYaml.java | 4 ++-- .../operator/utils/ParsedWeblogicOperatorYaml.java | 4 ++-- operator-build-maven-plugin/pom.xml | 2 +- .../kubernetes/operator/helpers/CrdHelper.java | 2 +- .../operator/helpers/PodStepContext.java | 2 +- .../weblogic/domain/model/DomainResource.java | 2 +- .../operator/builders/StubWatchFactory.java | 2 +- pom.xml | 14 +++++++------- 27 files changed, 49 insertions(+), 49 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index 35a8c3a5b8a..009c296f4cf 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -741,7 +741,7 @@ void testCreateNewCluster() { } /** - * Modify the domain scope property + * Modify the domain scope property. * From: "image: container-registry.oracle.com/middleware/weblogic:ImageTagBeingUsed" to * To: "image: container-registry.oracle.com/middleware/weblogic:DateAndTimeStamp" * e.g, From ""image: container-registry.oracle.com/middleware/weblogic:12.2.1.4" diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java index 9302ff91cf9..6c40c9f961a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java @@ -1235,7 +1235,7 @@ public static boolean buildAppArchive(AppParams params) { /** * Create an application archive that can be used by WebLogic Image Tool - * to create an image with coh-proxy-server.gar for testing Coherence use case + * to create an image with coh-proxy-server.gar for testing Coherence use case. * * @param params the parameters for creating a model-in-image image * @return true if the operation succeeds diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index 1c49d9c1003..5c96f44a3fb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -1885,7 +1885,7 @@ public static ExecResult removeWlsImageContainer(String containerName) { } /** - * Generate the model.sessmigr.yaml for a given test class + * Generate the model.sessmigr.yaml for a given test class. * * @param domainUid unique domain identifier * @param className test class name diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index b2fa561bcd8..80a6c6e6856 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -189,7 +189,7 @@ public static void downloadMonitoringExporterApp(String configFile, String appli } /** - * Build monitoring exporter web applicaiont wls-exporter.war with provided configuration + * Build monitoring exporter web applicaiont wls-exporter.war with provided configuration. * @param monitoringExporterSrcDir directory containing github monitoring exporter * @param configFile configuration file for weblogic domain monitoring * @param appDir directory where war file will be created diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesDisabledTestBase.java b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesDisabledTestBase.java index 503f02086c3..9f22d5332a9 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesDisabledTestBase.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesDisabledTestBase.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.create; @@ -10,7 +10,7 @@ /** * Tests that the artifacts in the yaml files that create-weblogic-operator.sh creates are correct * when all optional features are disabled: external rest disabled remote debug port disabled elk - * disabled no image pull secret + * disabled no image pull secret. */ public abstract class CreateOperatorGeneratedFilesOptionalFeaturesDisabledTestBase extends CreateOperatorGeneratedFilesTestBase { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesEnabledTestBase.java b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesEnabledTestBase.java index ff4f4a55ff6..13270876d01 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesEnabledTestBase.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesOptionalFeaturesEnabledTestBase.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2022, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.create; @@ -21,7 +21,7 @@ /** * Tests that the artifacts in the yaml files that create-weblogic-operator.sh creates are correct * when all optional features are enabled: external rest self signed cert remote debug port enabled - * elk enabled have image pull secret + * elk enabled have image pull secret. */ public abstract class CreateOperatorGeneratedFilesOptionalFeaturesEnabledTestBase extends CreateOperatorGeneratedFilesTestBase { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java index ab47942a397..c75df7a9cfc 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java @@ -80,7 +80,7 @@ /** * Base class for testing that the all artifacts in the yaml files that create-weblogic-operator.sh - * generates + * generates. */ abstract class CreateOperatorGeneratedFilesTestBase { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheSecurityYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheSecurityYaml.java index ab320f29259..c6c145f98ee 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheSecurityYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheSecurityYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -9,7 +9,7 @@ import io.kubernetes.client.openapi.models.V1ClusterRoleBinding; /** - * Parses a generated weblogic-domain-apache-security.yaml file into a set of typed k8s java objects + * Parses a generated weblogic-domain-apache-security.yaml file into a set of typed k8s java objects. */ public class ParsedApacheSecurityYaml extends ParsedKubernetesYaml { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheYaml.java index bbe624a2278..6e7a3397d1e 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedApacheYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -9,7 +9,7 @@ import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServiceAccount; -/** Parses a generated weblogic-domain-apache.yaml file into a set of typed k8s java objects */ +/** Parses a generated weblogic-domain-apache.yaml file into a set of typed k8s java objects. */ public class ParsedApacheYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedCreateWeblogicDomainJobYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedCreateWeblogicDomainJobYaml.java index f941fc372dd..4679db3dba7 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedCreateWeblogicDomainJobYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedCreateWeblogicDomainJobYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -8,7 +8,7 @@ import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1Job; -/** Parses a generated create-weblogic-domain-job.yaml file into a set of typed k8s java objects */ +/** Parses a generated create-weblogic-domain-job.yaml file into a set of typed k8s java objects. */ public class ParsedCreateWeblogicDomainJobYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDeleteWeblogicDomainJobYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDeleteWeblogicDomainJobYaml.java index 51c1553d46a..d4d3119beeb 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDeleteWeblogicDomainJobYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDeleteWeblogicDomainJobYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -8,7 +8,7 @@ import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1Job; -/** Parses a generated delete-weblogic-domain-job.yaml file into a set of typed k8s java objects */ +/** Parses a generated delete-weblogic-domain-job.yaml file into a set of typed k8s java objects. */ public class ParsedDeleteWeblogicDomainJobYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDomainCustomResourceYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDomainCustomResourceYaml.java index 3c60dad4772..50c9e3da749 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDomainCustomResourceYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedDomainCustomResourceYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2022, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -7,7 +7,7 @@ import oracle.kubernetes.weblogic.domain.model.DomainResource; -/** Parses a generated domain-custom-resource.yaml file into a set of typed k8s java objects */ +/** Parses a generated domain-custom-resource.yaml file into a set of typed k8s java objects. */ public class ParsedDomainCustomResourceYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikSecurityYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikSecurityYaml.java index 0ecee2fcb1f..484cb29424f 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikSecurityYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikSecurityYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -12,7 +12,7 @@ /** * Parses a generated * weblogic-domain-traefik-security-inputs.LegalNames.toDns1123LegalName(getClusterName()).yaml file - * into a set of typed k8s java objects + * into a set of typed k8s java objects. */ public class ParsedTraefikSecurityYaml extends ParsedKubernetesYaml { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikYaml.java index 503c0a3ac9d..bf059ab6124 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedTraefikYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -14,7 +14,7 @@ /** * Parses a generated * weblogic-domain-traefik-inputs.LegalNames.toDns1123LegalName(getClusterName()).yaml file into a - * set of typed k8s java objects + * set of typed k8s java objects. */ public class ParsedTraefikYaml extends ParsedKubernetesYaml { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerIngressYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerIngressYaml.java index 7f7d6d9437c..16b0babf1f5 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerIngressYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerIngressYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2022, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -9,7 +9,7 @@ import io.kubernetes.client.openapi.models.V1Service; /** - * Parses a generated weblogic-domain-voyager-ingress.yaml file into a set of typed k8s java objects + * Parses a generated weblogic-domain-voyager-ingress.yaml file into a set of typed k8s java objects. */ public class ParsedVoyagerIngressYaml extends ParsedKubernetesYaml { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorSecurityYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorSecurityYaml.java index ea186d4dccf..ce365696b13 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorSecurityYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorSecurityYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -10,7 +10,7 @@ import io.kubernetes.client.openapi.models.V1RoleBinding; import io.kubernetes.client.openapi.models.V1ServiceAccount; -/** Parses a generated voyager-operator-security.yaml file into a set of typed k8s java objects */ +/** Parses a generated voyager-operator-security.yaml file into a set of typed k8s java objects. */ public class ParsedVoyagerOperatorSecurityYaml extends ParsedKubernetesYaml { public ParsedVoyagerOperatorSecurityYaml(Path yamlPath) throws Exception { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorYaml.java index cd91d1049da..30e6234b405 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedVoyagerOperatorYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2022, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -10,7 +10,7 @@ import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Service; -/** Parses a generated voyager-operator.yaml file into a set of typed k8s java objects */ +/** Parses a generated voyager-operator.yaml file into a set of typed k8s java objects. */ public class ParsedVoyagerOperatorYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeClaimYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeClaimYaml.java index 5d403ae8a6f..9b5e8ce80dc 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeClaimYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeClaimYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -7,7 +7,7 @@ import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim; -/** Parses a generated weblogic-domain-pvc.yaml file into a set of typed k8s java objects */ +/** Parses a generated weblogic-domain-pvc.yaml file into a set of typed k8s java objects. */ public class ParsedWeblogicDomainPersistentVolumeClaimYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeYaml.java index 98cd831e3c5..48aab76c0c8 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicDomainPersistentVolumeYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2021, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -7,7 +7,7 @@ import io.kubernetes.client.openapi.models.V1PersistentVolume; -/** Parses a generated weblogic-domain-pv.yaml file into a set of typed k8s java objects */ +/** Parses a generated weblogic-domain-pv.yaml file into a set of typed k8s java objects. */ public class ParsedWeblogicDomainPersistentVolumeYaml extends ParsedKubernetesYaml { private DomainValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java index 4df6603bf2e..e0c20857e51 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -12,7 +12,7 @@ import io.kubernetes.client.openapi.models.V1RoleBinding; import io.kubernetes.client.openapi.models.V1ServiceAccount; -/** Parses a generated weblogic-operator-security.yaml file into a set of typed k8s java objects */ +/** Parses a generated weblogic-operator-security.yaml file into a set of typed k8s java objects. */ public class ParsedWeblogicOperatorSecurityYaml extends ParsedKubernetesYaml { private OperatorValues inputs; diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorYaml.java index d3025722f13..8cd5aa3515d 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorYaml.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -8,7 +8,7 @@ import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Service; -/** Parses a generated weblogic-operator.yaml file into a set of typed k8s java objects */ +/** Parses a generated weblogic-operator.yaml file into a set of typed k8s java objects. */ public class ParsedWeblogicOperatorYaml extends ParsedKubernetesYaml { private final OperatorValues inputs; diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 01ac2518ddf..5bf729b51a3 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -30,7 +30,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.13.1 + 3.15.0 org.junit.jupiter diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java index 4da8a2c6cad..3f4ce2fd15a 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java @@ -105,7 +105,7 @@ private CrdHelper() { } /** - * Used by build to generate crd-validation.yaml + * Used by build to generate crd-validation.yaml. * @param args Arguments that must be one value giving file name to create */ public static void main(String... args) throws URISyntaxException { diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index d0ee512d30c..32569905624 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -832,7 +832,7 @@ private V1SecretVolumeSource getRuntimeEncryptionSecretVolumeSource(String name) } /** - * Sets the environment variables used by operator/src/main/resources/scripts/startServer.sh + * Sets the environment variables used by operator/src/main/resources/scripts/startServer.sh. * @param vars a list to which new variables are to be added */ void addStartupEnvVars(List vars) { diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java index 4383203d326..f2ddac634d2 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java @@ -325,7 +325,7 @@ public FluentbitSpecification getFluentbitSpecification() { } /** - * Return the MII domain.spec.configuration.model.onlineUpdate.nonDynamicChangesMethod + * Return the MII domain.spec.configuration.model.onlineUpdate.nonDynamicChangesMethod. * @return {@link MIINonDynamicChangesMethod} */ public MIINonDynamicChangesMethod getMiiNonDynamicChangesMethod() { diff --git a/operator/src/test/java/oracle/kubernetes/operator/builders/StubWatchFactory.java b/operator/src/test/java/oracle/kubernetes/operator/builders/StubWatchFactory.java index a468dc93a48..91ff0a9afc9 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/builders/StubWatchFactory.java +++ b/operator/src/test/java/oracle/kubernetes/operator/builders/StubWatchFactory.java @@ -59,7 +59,7 @@ public static Memento install() throws NoSuchFieldException { } /** - * Adds the events to be returned from a single call to Watch.next() + * Adds the events to be returned from a single call to Watch.next(). * * @param events the events; will be converted to Watch.Response objects */ diff --git a/pom.xml b/pom.xml index 54a80fcc3b7..e5f0d76927e 100644 --- a/pom.xml +++ b/pom.xml @@ -670,17 +670,17 @@ 3.4.2 3.3.1 3.12.1 - 3.3.1 - 3.4.0 + 3.5.0 + 3.5.0 3.1.1 3.8.0 - 3.3.1 + 3.5.0 3.7.1 3.6.0 3.4.1 - 10.17.0 + 10.18.0 1.0 - 3.4.0 + 3.5.0 3.2.5 2.0.0.0 1.3.3 @@ -722,7 +722,7 @@ 2.11.0 10.0.3 2.0.16 - 1.5.6 + 1.5.7 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json @@ -731,7 +731,7 @@ 0.8.12 4.9.10 2.70.0 - 2.7.3 + 2.7.4 ${root.basedir}/build-tools/checkstyle/customized_google_checks.xml false From 8581620af649328ca485c9a951781c495565cf8c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 28 Aug 2024 14:53:06 -0400 Subject: [PATCH 145/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 5bf729b51a3..30144200d09 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -24,7 +24,7 @@ org.apache.maven maven-plugin-api - 3.9.8 + 3.9.9 provided diff --git a/pom.xml b/pom.xml index e5f0d76927e..5139ab12f4d 100644 --- a/pom.xml +++ b/pom.xml @@ -702,7 +702,7 @@ 4.2.2 19.0.1 3.0.1u2 - 2.0.10 + 2.0.20 4.12.0 3.9.0 1.78.1 From 4a5091d707689f555c6673e1e99ba3478bfd7d6c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 28 Aug 2024 17:49:36 -0400 Subject: [PATCH 146/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5139ab12f4d..b2f7719b626 100644 --- a/pom.xml +++ b/pom.xml @@ -669,7 +669,7 @@ 3.1.2 3.4.2 3.3.1 - 3.12.1 + 3.20.0 3.5.0 3.5.0 3.1.1 From 228643f9398e89dad902e9d2136f8e755e6a52c0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 29 Aug 2024 10:22:29 -0400 Subject: [PATCH 147/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b2f7719b626..d3057166195 100644 --- a/pom.xml +++ b/pom.xml @@ -666,16 +666,16 @@ 3.4.0 3.13.0 3.1.2 - 3.1.2 + 3.1.3 3.4.2 3.3.1 3.20.0 3.5.0 3.5.0 3.1.1 - 3.8.0 + 3.10.0 3.5.0 - 3.7.1 + 3.8.0 3.6.0 3.4.1 10.18.0 From 62ce242c63d9462985ac1a88637d431b7b711db1 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Thu, 29 Aug 2024 14:28:46 +0000 Subject: [PATCH 148/356] Add secure domain tests with 14.1.2.0.0 image --- Jenkinsfile.podman | 1 + .../kubernetes/ItSecureModeDomain.java | 858 ++++++++++++++++++ .../securemodeupgrade/mbean-prod-secure.yaml | 29 + .../mbeansecure-listenport-enabled.yaml | 33 + .../prod-global-ssl-enabled.yaml | 30 + .../prod-ssl-enabled-partial.yaml | 39 + .../securemodeupgrade/startmode-prod.yaml | 29 + .../startmode-secure-ssl-override.yaml | 36 + .../securemodeupgrade/startmode-secure.yaml | 31 + .../startsecure-listenport-enabled.yaml | 32 + 10 files changed, 1118 insertions(+) create mode 100644 integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSecureModeDomain.java create mode 100644 integration-tests/src/test/resources/securemodeupgrade/mbean-prod-secure.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/mbeansecure-listenport-enabled.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/prod-global-ssl-enabled.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/prod-ssl-enabled-partial.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/startmode-prod.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/startmode-secure-ssl-override.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/startmode-secure.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/startsecure-listenport-enabled.yaml diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 8bf873e8b8d..766dcd2dcdb 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -538,6 +538,7 @@ EOF MAVEN_PROFILE_NAME="integration-tests" echo "-Dit.test=\"${IT_TEST}\"" >> ${WORKSPACE}/.mvn/maven.config fi + echo "-Dmaven.wagon.http.retryHandler.count=3" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.wle.download.url=\"${wle_download_url}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.result.root=\"${result_root}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.pv.root=\"${pv_root}\"" >> ${WORKSPACE}/.mvn/maven.config diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSecureModeDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSecureModeDomain.java new file mode 100644 index 00000000000..1a8f70bfd17 --- /dev/null +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSecureModeDomain.java @@ -0,0 +1,858 @@ +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.weblogic.kubernetes; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1ContainerPort; +import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1Pod; +import io.kubernetes.client.util.Yaml; +import oracle.weblogic.domain.AuxiliaryImage; +import oracle.weblogic.domain.ClusterList; +import oracle.weblogic.domain.ClusterSpec; +import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.kubernetes.actions.impl.AppParams; +import oracle.weblogic.kubernetes.actions.impl.Cluster; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; +import oracle.weblogic.kubernetes.annotations.IntegrationTest; +import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecCommand; +import oracle.weblogic.kubernetes.utils.ExecResult; +import oracle.weblogic.kubernetes.utils.ImageUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; +import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME_DEFAULT; +import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; +import static oracle.weblogic.kubernetes.actions.TestActions.buildAppArchive; +import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; +import static oracle.weblogic.kubernetes.actions.TestActions.getDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.TestActions.getPod; +import static oracle.weblogic.kubernetes.actions.TestActions.listDomainCustomResources; +import static oracle.weblogic.kubernetes.actions.TestActions.shutdownDomain; +import static oracle.weblogic.kubernetes.assertions.TestAssertions.podDoesNotExist; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretsForImageRepos; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The test verifies various secure domains using 1412 image. + * Verify different combinations of production secure domain configurations can start. + * REST management interfaces are accessible thru appropriate channels. + * Verify deployed customer applications are accessible in appropriate channels and ports. + */ +@DisplayName("Test secure domains with 1412 image for a mii domain") +@IntegrationTest +@Tag("kind-parallel") +class ItSecureModeDomain { + + private static List namespaces; + private static String opNamespace; + private static String domainNamespace; + private static final int replicaCount = 1; + private static String domainUid; + private static final String adminServerName = "adminserver"; + private static final String clusterName = "mycluster"; + private static String adminServerPodName; + private String managedServerPrefix; + private static final String wlSecretName = "weblogic-credentials"; + private static final String encryptionSecretName = "encryptionsecret"; + + private final String imageTag1412 = "14.1.2.0.0-jdk17"; + private final String image1412 = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag1412; + private final String weblogicReady = "/weblogic/ready"; + private final String sampleAppUri = "/sample-war/index.jsp"; + + private static LoggingFacade logger = null; + + /** + * Install Operator. + * @param namespaces list of namespaces. + */ + @BeforeAll + public static void initAll(@Namespaces(9) List ns) { + logger = getLogger(); + namespaces = ns; + + // get a new unique opNamespace + logger.info("Assigning unique namespace for Operator"); + assertNotNull(namespaces.get(0), "Namespace list is null"); + opNamespace = namespaces.get(0); + + // install operator watching 6 domain namespaces + installAndVerifyOperator(opNamespace, namespaces.subList(1, 9).toArray(String[]::new)); + + // Create the repo secret to pull the image + // this secret is used only for non-kind cluster + namespaces.subList(1, 9).stream().forEach(ImageUtils::createTestRepoSecret); + } + + /** + * Shutdown domains after each test method. + */ + @AfterEach + void afterEach() { + if (listDomainCustomResources(domainNamespace).getItems().stream().anyMatch(dr + -> dr.getMetadata().getName().equals(domainUid))) { + DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + shutdownDomain(domainUid, domainNamespace); + logger.info("Checking that adminserver pod {0} does not exist in namespace {1}", + adminServerPodName, domainNamespace); + testUntil( + assertDoesNotThrow(() -> podDoesNotExist(adminServerPodName, domainUid, domainNamespace), + String.format("podDoesNotExist failed with ApiException for pod %s in namespace %s", + adminServerPodName, domainNamespace)), + logger, + "pod {0} to be deleted in namespace {1}", + adminServerPodName, + domainNamespace); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + testUntil(assertDoesNotThrow(() -> podDoesNotExist(managedServerPodName, domainUid, domainNamespace), + String.format("podDoesNotExist failed with ApiException for pod %s in namespace %s", + managedServerPodName, domainNamespace)), + logger, + "pod {0} to be deleted in namespace {1}", + managedServerPodName, + domainNamespace + ); + } + } + } + + /** + * Test starting a 14.1.2.0.0 domain with serverStartMode(prod). + * + * Verify the sample application is available in default port 7001. + * Verify the management REST interface is available in default port 7001. + * Verify the sample application available in cluster server in default port 7100. + * + */ + @Test + @DisplayName("Test starting a 14.1.2.0.0 domain with serverStartMode as production") + void testStartModeProduction() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(1); + domainUid = "testdomain1"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("startmode-prod.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default", 7001); + adminPorts.put("internal-t3", 7001); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default", 7001); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready and sample app available in port 7001 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + //verify secure channel is disabled + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", weblogicReady, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8100", "https", weblogicReady, "Connection refused", false)); + } + } + + + /** + * Test start secure domain with 14.1.2.0.0 image and ServerStartMode as secure. + * + * Verify all services are available only in HTTPS in adminserver as well as in managed servers. + * Verify the admin server sample application is available in default SSL port 7002. + * Verify the management REST interface is available in default admin port 9002. + * Verify the cluster sample application available in configured SSL port 8500. + * + */ + @Test + @DisplayName("Test start secure domain with 14.1.2.0.0 image and ServerStartMode as secure") + void testStartModeSecure() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(2); + domainUid = "testdomain2"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("startmode-secure.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default-secure", 7002); + adminPorts.put("default-admin", 9002); + adminPorts.put("internal-admin", 9002); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default-secure", 8500); + msPorts.put("default-admin", 9002); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready and sample app available in port 7001 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "9002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + //verify secure channel is disabled + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "9002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8500", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7100", "http", sampleAppUri, "Connection refused", false)); + } + } + + + /** + * Test start secure domain with 14.1.2.0.0 image and ServerStartMode as secure, disable SSL at domain level. + * + * Verify all services are available in HTTP, in adminserver as well as in managed servers. + * Verify the admin server sample application is available in configured listenport 7005. + * Verify the management REST interface is available in configured listenport 7005. + * Verify the sample application is available in cluster server default port 7100. + * + */ + @Test + @DisplayName("Test start secure domain with 14.1.2.0.0 image and ServerStartMode " + + "as secure disable SSL at domain level") + void testStartModeSecureOverrideSSL() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(3); + domainUid = "testdomain3"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("startmode-secure-ssl-override.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default", 7005); + adminPorts.put("internal-t3", 7005); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default", 7001); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready and sample app available in port 7001 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7005", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7005", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + //verify secure channel is disabled + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8100", "https", sampleAppUri, "Connection refused", false)); + } + } + + /** + * Test starting a 14.1.2.0.0 domain with production and secure mode enabled using MBean configuration. + * Verify the management REST interface is only available in + * default admin port 9002 in admin server and managed server. + * Verify the sample application is available in default SSL port 7002 in admin server. + * Verify the sample application is available in default SSL port in cluster server 8100. + * + */ + @Test + @DisplayName("Test starting a 14.1.2.0.0 domain with production and secure mode enabled using MBean configuration.") + void testMbeanProductionSecureMBeanConfiguration() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(4); + domainUid = "testdomain4"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("mbean-prod-secure.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default-secure", 7002); + adminPorts.put("default-admin", 9002); + adminPorts.put("internal-admin", 9002); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default-secure", 7002); + msPorts.put("default-admin", 9002); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready and sample app available in port 7001 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "9002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + //verify listenport is disabled + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "9002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7002", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", sampleAppUri, "Connection refused", false)); + } + } + + /** + * Test start domain with 14.1.2.0.0 image and SSLEnabled at domain level with start mode prod. + * + * Verify the admin server sample application is available in port 7001 and in SSL port 7002. + * Verify the management REST interface available in ports 7001 and SSL port 7002. + * Verify the management REST interface available in cluster in ports 7100 and SSL port 8100. + * Verify the cluster sample application available in ports 7100 and HTTPS 8100. + * + */ + @Test + @DisplayName("Test start domain with 14.1.2.0.0 image and SSLEnabled at domain level with start mode prod.") + void testStartmodeProductionSSLEnabledGlobal() throws UnknownHostException, ApiException { + + domainNamespace = namespaces.get(5); + domainUid = "testdomain5"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("prod-global-ssl-enabled.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default", 7001); + adminPorts.put("default-secure", 7002); + adminPorts.put("internal-t3", 7001); + adminPorts.put("internal-t3s", 7002); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default-secure", 7002); + msPorts.put("default", 7001); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready and sample app available in port 7001 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7002", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + } + } + + /** + * Test start domain with 14.1.2.0.0 image, secure mode disabled in MBean, enable SSL in adminserver only. + * + * Verify admin server starts with 2 listen ports non ssl at 7001 and SSL at 7002. + * Verify the admin server sample application is available in ports 7001 and 7002. + * Verify the management REST interface available in 7001 and 7002 + * Verify the cluster sample application available in configured listenport 8001. + * Verify the management REST interface available in cluster in configured listenport 8001. + * + */ + @Test + @DisplayName("Test start domain with 14.1.2.0.0 image, secure mode disabled in MBean, " + + "enable SSL at adminserver level.") + void testProductionSSLEnabledPartial() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(6); + domainUid = "testdomain6"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("prod-ssl-enabled-partial.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default", 7001); + adminPorts.put("internal-t3", 7001); + adminPorts.put("default-secure", 7002); + adminPorts.put("internal-t3s", 7002); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default", 8001); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready is available in port 7001 and 7002 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "9002", "https", weblogicReady, "Connection refused", false)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "9002", "http", weblogicReady, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8100", "https", sampleAppUri, "Connection refused", false)); + } + } + + /** + * Test start domain with 14.1.2.0.0 image, secure mode enabled in MBean, disable SSL at domain level, + * disable admin port, enable listenport at domain level. + * + * Verify admin server starts with only 1 listen port, non ssl at 7001. + * Verify the admin server sample application is available in port 7001. + * Verify the management REST interface available in 7001. + * Verify the cluster sample application available in port 7100. + * + */ + @Test + @DisplayName("Test start domain with 14.1.2.0.0 image, secure mode disabled in MBean, " + + "enable listenport at domain level") + void testSecureSSLDisabledListenportEnabled() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(7); + domainUid = "testdomain7"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("mbeansecure-listenport-enabled.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default", 7001); + adminPorts.put("internal-t3", 7001); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default", 7001); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready is available in port 7001 and 7002 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "9002", "https", weblogicReady, "Connection refused", false)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "9002", "https", weblogicReady, "Connection refused", false)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8100", "https", sampleAppUri, "Connection refused", false)); + } + } + + /** + * Test start domain with 14.1.2.0.0 image, secure mode enabled in MBean, disable SSL at domain level, + * enable listenport at domain level. + * + * Verify admin server starts with 2 listen ports non ssl at 7001 and admin SSL port 9002. + * Verify the admin server sample application is available in port 7001. + * Verify the management REST interface available only in adminport 9002. + * Verify the cluster sample application available in listenport 7100. + * + */ + @Test + @DisplayName("Test start domain with 14.1.2.0.0 image, secure mode disabled in MBean, " + + "disable SSL at domain level.") + void testStartSecureSSLDisabledListenportEnabled() throws UnknownHostException, ApiException { + domainNamespace = namespaces.get(8); + domainUid = "testdomain8"; + adminServerPodName = domainUid + "-" + adminServerName; + managedServerPrefix = domainUid + "-" + clusterName + "-ms-"; + + createDomain("startsecure-listenport-enabled.yaml"); + dumpResources(); + + Map adminPorts = new HashMap<>(); + adminPorts.put("default", 7001); + adminPorts.put("default-admin", 9002); + adminPorts.put("internal-admin", 9002); + verifyServerChannels(domainNamespace, adminServerPodName, adminPorts); + + Map msPorts = new HashMap<>(); + msPorts.put("default", 7001); + msPorts.put("default-admin", 9002); + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + verifyServerChannels(domainNamespace, managedServerPodName, msPorts); + } + + //verify /weblogic/ready is available in port 7001 and 7002 + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "9002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, adminServerPodName, + "7002", "https", sampleAppUri, "Connection refused", false)); + + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "9002", "https", weblogicReady, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "7001", "http", sampleAppUri, "HTTP/1.1 200 OK", true)); + assertTrue(verifyServerAccess(domainNamespace, managedServerPodName, + "8100", "https", sampleAppUri, "Connection refused", false)); + } + } + + private DomainResource createDomain(String wdtModel) { + // create WDT properties file for the WDT model + Path wdtVariableFile = Paths.get(WORK_DIR, this.getClass().getSimpleName(), "wdtVariable.properties"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile); + Files.createDirectories(wdtVariableFile.getParent()); + Files.writeString(wdtVariableFile, "DomainName=" + domainUid + "\n", StandardOpenOption.CREATE); + }); + + String auxImageName = DOMAIN_IMAGES_PREFIX + "dci-securedomain-image"; + String auxImageTag = getDateAndTimeStamp(); + Path wdtModelFile = Paths.get(RESOURCE_DIR, "securemodeupgrade", wdtModel); + + // create auxiliary domain creation image + String auxImage = createAuxImage(auxImageName, auxImageTag, wdtModelFile.toString(), wdtVariableFile.toString()); + + //create a MII domain resource with the auxiliary image + DomainResource domain = createDomainUsingAuxiliaryImage(domainNamespace, domainUid, + clusterName, image1412, auxImage); + return domain; + } + + private static void dumpResources() throws ApiException { + DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + getConfigXML(); + logger.info(Yaml.dump(dcr)); + logger.info(Yaml.dump(getPod(domainNamespace, null, adminServerPodName))); + logger.info(Yaml.dump(getPod(domainNamespace, null, domainUid + "-" + clusterName + "-ms-1"))); + } + + + /** + * Create domain custom resource with auxiliary image, base image. + * + * @param domainNamespace namespace in which to create domain + * @param domainUid domain id + * @param baseImage base image used by the WebLogic pods + * @param auxImage auxiliary image containing domain creation WDT model and properties files + * @return domain resource object + */ + private DomainResource createDomainUsingAuxiliaryImage(String domainNamespace, String domainUid, String clusterName, + String baseImage, String auxImage) { + // create secret for admin credentials + logger.info("Create secret for admin credentials"); + createSecretWithUsernamePassword(wlSecretName, domainNamespace, + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + // create encryption secret + logger.info("Create encryption secret"); + createSecretWithUsernamePassword(encryptionSecretName, domainNamespace, + ENCRYPION_USERNAME_DEFAULT, ENCRYPION_PASSWORD_DEFAULT); + + // create domain custom resource using a auxiliary image + logger.info("Creating domain custom resource with domainUid {0} and auxiliary images {1}", + domainUid, auxImage); + DomainResource domainCR = createDomainResource(domainUid, domainNamespace, auxImage, + baseImage, wlSecretName, createSecretsForImageRepos(domainNamespace), + encryptionSecretName, replicaCount, clusterName); + + // create domain and verify its running + logger.info("Creating domain {0} with auxiliary images {1} in namespace {2}", + domainUid, auxImage, domainNamespace); + createDomainAndVerify(domainUid, domainCR, domainNamespace, adminServerPodName, + managedServerPrefix, replicaCount); + return domainCR; + } + + /** + * Create auxiliary image. + * + * @param imageName name of the auxiliary image + * @param imageTag auxiliary image tag + * @param wdtModelFile WDT model file + * @param wdtVariableFile WDT property file + * @return name of the auxiliary image created + */ + private String createAuxImage(String imageName, String imageTag, String wdtModelFile, String wdtVariableFile) { + // build sample-app application + AppParams appParams = defaultAppParams() + .srcDirList(Collections.singletonList(MII_BASIC_APP_NAME)) + .appArchiveDir(ARCHIVE_DIR + this.getClass().getSimpleName()) + .appName(MII_BASIC_APP_NAME); + assertTrue(buildAppArchive(appParams), + String.format("Failed to create app archive for %s", MII_BASIC_APP_NAME)); + List archiveList = Collections.singletonList(appParams.appArchiveDir() + "/" + MII_BASIC_APP_NAME + ".zip"); + + //create an auxilary image with model and sample-app application + WitParams witParams + = new WitParams() + .modelImageName(imageName) + .modelImageTag(imageTag) + .modelFiles(Arrays.asList(wdtModelFile)) + .modelVariableFiles(Arrays.asList(wdtVariableFile)) + .modelArchiveFiles(archiveList); + createAndPushAuxiliaryImage(imageName, imageTag, witParams); + + return imageName + ":" + imageTag; + } + + private static DomainResource createDomainResource( + String domainResourceName, + String domNamespace, + String auxImageName, + String imageName, + String adminSecretName, + String[] repoSecretNames, + String encryptionSecretName, + int replicaCount, + String clusterName) { + + // create secrets + List secrets = new ArrayList<>(); + for (String secret : repoSecretNames) { + secrets.add(new V1LocalObjectReference().name(secret)); + } + + // create the domain CR + DomainResource domain = new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new io.kubernetes.client.openapi.models.V1ObjectMeta() + .name(domainResourceName) + .namespace(domNamespace)) + .spec(new oracle.weblogic.domain.DomainSpec() + .domainUid(domainResourceName) + .domainHomeSourceType("FromModel") + .image(imageName) + .imagePullPolicy(IMAGE_PULL_POLICY) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(adminSecretName)) + .imagePullSecrets(secrets) + .includeServerOutInPodLog(true) + .serverStartPolicy("IfNeeded") + .serverPod(new oracle.weblogic.domain.ServerPod() + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value(SSL_PROPERTIES)) + .addEnvItem(new V1EnvVar() + .name("WLSDEPLOY_PROPERTIES") + .value(SSL_PROPERTIES)) + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true")) + .addEnvItem(new io.kubernetes.client.openapi.models.V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom "))) + .configuration(new oracle.weblogic.domain.Configuration() + .model(new oracle.weblogic.domain.Model() + .withAuxiliaryImage(new AuxiliaryImage() + .image(auxImageName) + .imagePullPolicy(IMAGE_PULL_POLICY) + .sourceWDTInstallHome("/auxiliary/weblogic-deploy") + .sourceModelHome("/auxiliary/models")) + .domainType("WLS") + .runtimeEncryptionSecret(encryptionSecretName)) + .introspectorJobActiveDeadlineSeconds(3000L))); + + ClusterList clusters = Cluster.listClusterCustomResources(domNamespace); + + if (clusterName != null) { + String clusterResName = clusterName; + if (clusters.getItems().stream().anyMatch(cluster -> cluster.getClusterName().equals(clusterResName))) { + getLogger().info("!!!Cluster {0} in namespace {1} already exists, skipping...", clusterResName, domNamespace); + } else { + getLogger().info("Creating cluster {0} in namespace {1}", clusterResName, domNamespace); + ClusterSpec spec + = new ClusterSpec().withClusterName(clusterName).replicas(replicaCount).serverStartPolicy("IfNeeded"); + createClusterAndVerify(createClusterResource(clusterResName, domNamespace, spec)); + } + // set cluster references + domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterResName)); + } + setPodAntiAffinity(domain); + return domain; + } + + private void verifyServerChannels(String domainNamespace, String podName, + Map portsExpected) throws ApiException { + logger.info("Verifying server channels in pod {0}", podName); + //get the pod + V1Pod pod = getPod(domainNamespace, null, podName); + assertNotNull(pod); + + //verify if all the container port names and numbers are in the expected list + Map ports = pod.getSpec().getContainers().stream() + .filter(container -> container.getName().equals("weblogic-server")) + .findFirst().get().getPorts().stream() + .collect(Collectors.toMap(V1ContainerPort::getName, V1ContainerPort::getContainerPort)); + logger.info(ports.toString()); + assertTrue(ports.equals(portsExpected), "Didn't get the correct container ports " + + "Expected " + portsExpected.toString() + " Got " + ports.toString()); + } + + private static boolean verifyServerAccess(String namespace, String podName, String port, String protocol, + String uri, String expected, boolean checkHttpresponseCode) { + boolean success = false; + + String url = protocol + "://" + podName + ":" + port + uri; + String curlCmd = " -- curl -vkgs --noproxy '*' " + url; + logger.info("Checking the server access at {0}", curlCmd); + String command = KUBERNETES_CLI + " exec -n " + namespace + " " + podName + curlCmd; + + ExecResult result = null; + try { + result = ExecCommand.exec(command, true); + } catch (IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + assertNotNull(result, "result is null"); + String response = result.stdout().trim(); + logger.info(response); + logger.info(result.stderr()); + logger.info("{0}", result.exitValue()); + if (checkHttpresponseCode) { + if (result.stderr().trim().contains("HTTP/1.1 200 OK") + || result.stderr().trim().contains("HTTP/2 200")) { + success = true; + } else { + logger.info("Didn't get the expected http response code"); + } + } else { + if (result.stderr().trim().contains(expected) + || result.stdout().trim().contains(expected)) { + logger.info("Got the expected server response"); + success = true; + } else { + logger.info("Didn't get the expected server response {0}", expected); + } + } + return success; + } + + private static void getConfigXML() { + String curlCmd = " cat /u01/domains/" + domainUid + "/config/config.xml"; + logger.info("Dumping config.xml the server access at {0}", curlCmd); + String command = KUBERNETES_CLI + " exec -n " + domainNamespace + " " + adminServerPodName + curlCmd; + ExecResult result = null; + try { + result = ExecCommand.exec(command, true); + } catch (IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + logger.info(result.stdout().trim()); + logger.info(result.stderr().trim()); + } + +} diff --git a/integration-tests/src/test/resources/securemodeupgrade/mbean-prod-secure.yaml b/integration-tests/src/test/resources/securemodeupgrade/mbean-prod-secure.yaml new file mode 100644 index 00000000000..f589ce609c4 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/mbean-prod-secure.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' +topology: + Name: '@@PROP:DomainName@@' + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false + ProductionModeEnabled: true +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/mbeansecure-listenport-enabled.yaml b/integration-tests/src/test/resources/securemodeupgrade/mbeansecure-listenport-enabled.yaml new file mode 100644 index 00000000000..4dff4392c73 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/mbeansecure-listenport-enabled.yaml @@ -0,0 +1,33 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' +topology: + Name: '@@PROP:DomainName@@' + AdministrationPortEnabled: false + ListenPortEnabled: true + SSLEnabled: false + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + Cluster: + mycluster: + SecureReplicationEnabled: false + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false + ProductionModeEnabled: true +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/prod-global-ssl-enabled.yaml b/integration-tests/src/test/resources/securemodeupgrade/prod-global-ssl-enabled.yaml new file mode 100644 index 00000000000..66f80a19de7 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/prod-global-ssl-enabled.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'prod' +topology: + Name: '@@PROP:DomainName@@' + SSLEnabled: true + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/prod-ssl-enabled-partial.yaml b/integration-tests/src/test/resources/securemodeupgrade/prod-ssl-enabled-partial.yaml new file mode 100644 index 00000000000..adc645492cd --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/prod-ssl-enabled-partial.yaml @@ -0,0 +1,39 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' +topology: + Name: '@@PROP:DomainName@@' + AdminServerName: adminserver + Server: + adminserver: + ListenPort: 7001 + SSL: + Enabled: true + ListenPort: 7002 + ServerTemplate: + myserver-template: + Cluster: mycluster + ListenPort: 8001 + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false + ProductionModeEnabled: true + SecurityConfiguration: + SecureMode: + SecureModeEnabled: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/startmode-prod.yaml b/integration-tests/src/test/resources/securemodeupgrade/startmode-prod.yaml new file mode 100644 index 00000000000..004684c65dd --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/startmode-prod.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'prod' +topology: + Name: '@@PROP:DomainName@@' + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/startmode-secure-ssl-override.yaml b/integration-tests/src/test/resources/securemodeupgrade/startmode-secure-ssl-override.yaml new file mode 100644 index 00000000000..86e2b1c1510 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/startmode-secure-ssl-override.yaml @@ -0,0 +1,36 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'secure' +topology: + Name: '@@PROP:DomainName@@' + AdministrationPortEnabled: false + ListenPortEnabled: true + SSLEnabled: false + AdminServerName: adminserver + Server: + adminserver: + ListenPort: 7005 + ServerTemplate: + myserver-template: + Cluster: mycluster + Cluster: + mycluster: + SecureReplicationEnabled: false + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/startmode-secure.yaml b/integration-tests/src/test/resources/securemodeupgrade/startmode-secure.yaml new file mode 100644 index 00000000000..88dc8cff1d9 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/startmode-secure.yaml @@ -0,0 +1,31 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'secure' +topology: + Name: '@@PROP:DomainName@@' + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + SSL: + ListenPort: 8500 + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/startsecure-listenport-enabled.yaml b/integration-tests/src/test/resources/securemodeupgrade/startsecure-listenport-enabled.yaml new file mode 100644 index 00000000000..b63c2689875 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/startsecure-listenport-enabled.yaml @@ -0,0 +1,32 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'secure' +topology: + Name: '@@PROP:DomainName@@' + ListenPortEnabled: true + SSLEnabled: false + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + Cluster: + mycluster: + SecureReplicationEnabled: false + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file From 52cdb1fb5350832bb56dde999fcc83345c30f03c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 29 Aug 2024 14:29:54 +0000 Subject: [PATCH 149/356] Merge branch 'rm/fix-WRC-command' into 'main' update WRC extension info See merge request weblogic-cloud/weblogic-kubernetes-operator!4796 (cherry picked from commit 31af2be62e680ebd089a30b57885587b22dfb93c) 85f8b16f update WRC extension info --- .../accessing-the-domain/remote-admin-console.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md b/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md index dafe6c2a2d2..e89970945fe 100644 --- a/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md +++ b/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md @@ -45,14 +45,14 @@ To set up access to WebLogic Server domains running in Kubernetes using the Remo **NOTE**: These instructions assume that you are installing and running the Remote Console externally to your Kubernetes cluster. -1. For [additional functionality](https://oracle.github.io/weblogic-remote-console/setup/console/#ext), incorporate and deploy the WebLogic Remote Console extension in your 12.2.1.4 and 14.1.1 domains. +1. For [additional functionality](https://oracle.github.io/weblogic-remote-console/setup/console/#ext), incorporate and deploy the WebLogic Remote Console extension in your 12.2.1.4 and 14.1.1 domains. **NOTE**: As a best practice, make sure that you are using the same versions of the WebLogic Remote Console and the WebLogic Remote Console Extension, otherwise you might lose functionality. a. From [https://github.com/oracle/weblogic-remote-console/releases](https://github.com/oracle/weblogic-remote-console/releases), download the Remote Console extension WAR file, [console-rest-ext-[version].war](https://github.com/oracle/weblogic-remote-console/releases/download/v2.4.10/console-rest-ext-9.0.war). b. Using the WebLogic Deploy Tooling (WDT) Archive Helper Tool, modify the WDT application archive to include the Remote Console Extension downloaded in the previous step. For example: ``` - /Directory to WDT/weblogic-deploy/bin/archiveHelper.sh add weblogicRemoteConsoleExtension archive_file=/Directory to WDT application archive/archive.zip -source=/Directory to Remote Console Extension/console-rest-ext[version].war + /Directory to WDT/weblogic-deploy/bin/archiveHelper.sh add weblogicRemoteConsoleExtension -archive_file=/Directory to WDT application archive/archive.zip -source=/Directory to Remote Console Extension/console-rest-ext[version].war ``` For more information, see the [Archive Helper Tool](https://oracle.github.io/weblogic-deploy-tooling/userguide/tools/archive_helper/) documentation. From 50da6a47f00a400ea9677457fc5e80952ec9b807 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 29 Aug 2024 10:32:19 -0400 Subject: [PATCH 150/356] Prepare for WKO 4.2.6 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 9474816cdd0..91e6db65f6c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index c945a9bbe3e..d84a4957bfb 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 1f488cb3567..12ce863844c 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index f34cd6d0a15..c2ca6be445b 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.6-SNAPSHOT + 4.2.6 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 9742838c8bf..22a1081f167 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 30144200d09..4ca755e7de6 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.6-SNAPSHOT + 4.2.6 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index c646afee3c1..8c9dddebdb6 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index d3057166195..73d5be81ccf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 567988d50ca..5d065e93870 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6-SNAPSHOT + 4.2.6 operator-swagger From a1464cb81c1b71a2d98c48f3665f7cfc489cba38 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 29 Aug 2024 10:53:30 -0400 Subject: [PATCH 151/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73d5be81ccf..0ad257185a9 100644 --- a/pom.xml +++ b/pom.xml @@ -665,7 +665,7 @@ 3.5.0 3.4.0 3.13.0 - 3.1.2 + 3.1.3 3.1.3 3.4.2 3.3.1 From d6b95a1f19d856de74b6c7aad7f7d43764b4df55 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 3 Sep 2024 08:51:16 -0400 Subject: [PATCH 152/356] Use GITHUB_TOKEN --- .github/workflows/publish-latest-minor-gh-pages.yml | 2 +- .github/workflows/release.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-latest-minor-gh-pages.yml b/.github/workflows/publish-latest-minor-gh-pages.yml index cfd80e12a08..600627e4326 100644 --- a/.github/workflows/publish-latest-minor-gh-pages.yml +++ b/.github/workflows/publish-latest-minor-gh-pages.yml @@ -34,7 +34,7 @@ jobs: with: ref: gh-pages path: gh-pages - token: ${{ secrets.PUBLISH_SECRET }} + token: ${{ secrets.GITHUB_TOKEN }} - name: Build and publish site run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca17f1b1d4a..26438b34973 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: - name: Build env: - GITHUB_TOKEN: ${{ secrets.PUBLISH_SECRET }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mvn clean package -Dtag=${{ env.VERSION }} cd kubernetes/samples/scripts @@ -71,7 +71,7 @@ jobs: with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} - password: ${{ secrets.PUBLISH_SECRET }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push container image uses: docker/build-push-action@v6 @@ -87,7 +87,7 @@ jobs: with: ref: gh-pages path: gh-pages - token: ${{ secrets.PUBLISH_SECRET }} + token: ${{ secrets.GITHUB_TOKEN }} - name: Generate and publish Helm chart run: | From 50114ce500c724351ef7fbdd4f1b0a810b76aeb2 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 3 Sep 2024 10:23:06 -0400 Subject: [PATCH 153/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 4ca755e7de6..2a80271b476 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -59,7 +59,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.13.1 + 3.15.0 oracle.kubernetes:jsonschema-maven-plugin diff --git a/pom.xml b/pom.xml index 0ad257185a9..df269675527 100644 --- a/pom.xml +++ b/pom.xml @@ -678,7 +678,7 @@ 3.8.0 3.6.0 3.4.1 - 10.18.0 + 10.18.1 1.0 3.5.0 3.2.5 @@ -718,9 +718,9 @@ 0.16.0 2.17.2 2.17.2 - 2.2 + 2.3 2.11.0 - 10.0.3 + 10.0.4 2.0.16 1.5.7 ${project.basedir}/src-generated-swagger From 344f245d14c446069faa6afe9e61f304f1bf60b4 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Fri, 6 Sep 2024 17:11:51 +0000 Subject: [PATCH 154/356] Upgrade14120 fixes rel42 --- .../src/main/resources/scripts/modelInImage.sh | 12 +++++------- .../main/resources/scripts/model_wdt_mii_filter.py | 14 ++++++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 8c1fa284baa..bc9bf2d6b19 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -399,13 +399,12 @@ createWLDomain() { if [ -f ${PRIMORDIAL_DOMAIN_ZIPPED} ] ; then checkSecureModeForUpgrade fi - if [ ${WDT_ARTIFACTS_CHANGED} -ne 0 ] || [ ${jdk_changed} -eq 1 ] \ || [ ${SECRETS_AND_ENV_CHANGED} -ne 0 ] || [ ${DISABLE_SM_FOR_12214_NONSM_UPG} -eq 1 ] ; then - trace "Need to create domain ${WDT_DOMAIN_TYPE}" createModelDomain - if [ "${MERGED_MODEL_ENVVARS_SAME}" == "false" ] ; then + if [ "${MERGED_MODEL_ENVVARS_SAME}" == "false" ] || [ ${DISABLE_SM_FOR_12214_NONSM_UPG} -eq 1 ] ; then + # Make sure it will run the introspectDomain.py DOMAIN_CREATED=1 fi else @@ -500,7 +499,8 @@ createModelDomain() { trace "Entering createModelDomain" createPrimordialDomain - if [ "${MERGED_MODEL_ENVVARS_SAME}" == "false" ] ; then + # If model changes or upgrade image scenario, run update domain. + if [ "${MERGED_MODEL_ENVVARS_SAME}" == "false" ] || [ $DISABLE_SM_FOR_12214_NONSM_UPG -eq 1 ] ; then # if there is a new primordial domain created then use newly created primordial domain otherwise # if the primordial domain already in the configmap, restore it # @@ -780,9 +780,6 @@ createPrimordialDomain() { trace "No primordial domain or need to create again because of changes require domain recreation" - #if } -eq 1 ] && [ -f ${PRIMORDIAL_DOMAIN_ZIPPED} ]; then - - wdtCreatePrimordialDomain create_primordial_tgz=1 MII_USE_ONLINE_UPDATE=false @@ -846,6 +843,7 @@ checkSecureModeForUpgrade() { if [ -f /tmp/mii_domain_upgrade.txt ] && [ $(grep -i False /tmp/mii_domain_upgrade.txt | wc -l ) -gt 0 ] ; then if [ -f /tmp/mii_domain_before14120.txt ] && [ $(grep -i True /tmp/mii_domain_before14120.txt | wc -l ) -gt 0 ] ; then # Set this so that the upgrade image only scenario 12.2.1.4 to 14.1.2 will recreate the domain + trace "Domain version is earlier than 14.1.2, upgrade only image to 14.1.2 detected" DISABLE_SM_FOR_12214_NONSM_UPG=1 fi fi diff --git a/operator/src/main/resources/scripts/model_wdt_mii_filter.py b/operator/src/main/resources/scripts/model_wdt_mii_filter.py index 7feaa85736a..cc653966c00 100644 --- a/operator/src/main/resources/scripts/model_wdt_mii_filter.py +++ b/operator/src/main/resources/scripts/model_wdt_mii_filter.py @@ -183,6 +183,9 @@ def filter_model(model): try: if model is not None: + + upgradeServerIfNeeded(model) + if getOfflineWlstEnv() is None: initOfflineWlstEnv(model) @@ -216,7 +219,6 @@ def filter_model(model): if 'ServerTemplate' in topology: customizeServerTemplates(model) - upgradeServerIfNeeded(model) except: exc_type, exc_obj, exc_tb = sys.exc_info() ee_string = traceback.format_exception(exc_type, exc_obj, exc_tb) @@ -413,14 +415,18 @@ def upgradeServerIfNeeded(model): # if domainInfo already set to secure or in dev mode then do not set it, prod mode will not be secure # regardless of others # if the model disabled `ProductionModeEnabled` specifically now, do nothing - + prod_mode = False if 'domainInfo' in model and 'ServerStartMode' in model['domainInfo']: - return + mode = model['domainInfo']['ServerStartMode'] + if mode == 'secure' or mode == 'dev': + return + else: + prod_mode = True if 'topology' in model: topology = model['topology'] - if 'ProductionModeEnabled' not in topology: + if not prod_mode and 'ProductionModeEnabled' not in topology: return if 'ProductionModeEnabled' in topology: From 8e619e96b33f928605fcbb3e9e74b553988e0d81 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 10 Sep 2024 17:18:06 +0000 Subject: [PATCH 155/356] Correctly record existing resources during operator start --- .../operator/DomainResourcesValidation.java | 15 ++++++--------- .../kubernetes/operator/DomainPresenceTest.java | 10 +++++----- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java b/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java index 35fc1011ce6..c2ce491d875 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -154,7 +154,7 @@ private void addOperatorEventList(CoreV1EventList list) { private void addPod(V1Pod pod) { String domainUid = PodHelper.getPodDomainUid(pod); String serverName = PodHelper.getPodServerName(pod); - DomainPresenceInfo info = getExistingDomainPresenceInfo(domainUid); + DomainPresenceInfo info = getOrComputeDomainPresenceInfo(domainUid); Optional.ofNullable(info).ifPresent(i -> i.addServerNameFromPodList(serverName)); if (domainUid != null && serverName != null) { @@ -173,10 +173,6 @@ private DomainPresenceInfo getOrComputeDomainPresenceInfo(String domainUid) { return getDomainPresenceInfoMap().computeIfAbsent(domainUid, k -> new DomainPresenceInfo(namespace, domainUid)); } - private DomainPresenceInfo getExistingDomainPresenceInfo(String domainUid) { - return getDomainPresenceInfoMap().get(domainUid); - } - private Map getDomainPresenceInfoMap() { return processor.getDomainPresenceInfoMap().computeIfAbsent(namespace, k -> new ConcurrentHashMap<>()); } @@ -192,7 +188,7 @@ private void addServiceList(V1ServiceList list) { private void addService(V1Service service) { String domainUid = ServiceHelper.getServiceDomainUid(service); if (domainUid != null) { - ServiceHelper.addToPresence(getExistingDomainPresenceInfo(domainUid), service); + ServiceHelper.addToPresence(getOrComputeDomainPresenceInfo(domainUid), service); } } @@ -203,7 +199,7 @@ private void addPodDisruptionBudgetList(V1PodDisruptionBudgetList list) { private void addPodDisruptionBudget(V1PodDisruptionBudget pdb) { String domainUid = PodDisruptionBudgetHelper.getDomainUid(pdb); if (domainUid != null) { - PodDisruptionBudgetHelper.addToPresence(getExistingDomainPresenceInfo(domainUid), pdb); + PodDisruptionBudgetHelper.addToPresence(getOrComputeDomainPresenceInfo(domainUid), pdb); } } @@ -231,7 +227,8 @@ private void addDomain(DomainResource domain) { DomainPresenceInfo cachedInfo = getDomainPresenceInfoMap().get(domain.getDomainUid()); if (domain.getStatus() == null) { newDomainNames.add(domain.getDomainUid()); - } else if (cachedInfo != null && domain.isGenerationChanged(cachedInfo.getDomain())) { + } else if (cachedInfo != null && cachedInfo.getDomain() != null + && domain.isGenerationChanged(cachedInfo.getDomain())) { modifiedDomainNames.add(domain.getDomainUid()); } getOrComputeDomainPresenceInfo(domain.getDomainUid()).setDomain(domain); diff --git a/operator/src/test/java/oracle/kubernetes/operator/DomainPresenceTest.java b/operator/src/test/java/oracle/kubernetes/operator/DomainPresenceTest.java index 0e9bb4dc991..d40a924f6df 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DomainPresenceTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DomainPresenceTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -410,7 +410,7 @@ void whenDomainStatusBecameNull_generateDomainCreatedEvent() { } @Test - void whenK8sHasOneDomainWithMissingInfo_dontRecordAdminServerService() { + void whenK8sHasOneDomainWithMissingInfo_recordAdminServerService() { addDomainResource(UID1, NS); V1Service service = createServerService(UID1, NS, "admin"); testSupport.defineResources(service); @@ -418,7 +418,7 @@ void whenK8sHasOneDomainWithMissingInfo_dontRecordAdminServerService() { testSupport.addComponent("DP", DomainProcessor.class, dp); testSupport.runSteps(domainNamespaces.readExistingResources(NS, dp)); - assertThat(getDomainPresenceInfo(dp, UID1).getServerService("admin"), equalTo(null)); + assertThat(getDomainPresenceInfo(dp, UID1).getServerService("admin"), equalTo(service)); } @Test @@ -466,7 +466,7 @@ void whenK8sDomainWithMoreThanCallRequestLimitNumberOfPods_recordPodsPresence() } @Test - void whenK8sHasOneDomainWithPodButMissingInfo_dontRecordPodPresence() { + void whenK8sHasOneDomainWithPodButMissingInfo_recordPodPresence() { addDomainResource(UID1, NS); V1Pod pod = createPodResource(UID1, NS, "admin"); testSupport.defineResources(pod); @@ -474,7 +474,7 @@ void whenK8sHasOneDomainWithPodButMissingInfo_dontRecordPodPresence() { testSupport.addComponent("DP", DomainProcessor.class, dp); testSupport.runSteps(domainNamespaces.readExistingResources(NS, dp)); - assertThat(getDomainPresenceInfo(dp, UID1).getServerPod("admin"), equalTo(null)); + assertThat(getDomainPresenceInfo(dp, UID1).getServerPod("admin"), equalTo(pod)); } @Test From 0e93fc8d9060eda3962aeab4b4041072fe1e938d Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 10 Sep 2024 18:20:26 -0400 Subject: [PATCH 156/356] Ensure operator can shut down quickly --- .../main/java/oracle/kubernetes/operator/BaseMain.java | 3 ++- .../java/oracle/kubernetes/operator/work/Engine.java | 9 +++++++-- .../kubernetes/operator/work/FiberTestSupport.java | 7 ++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java b/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java index 32f539233dd..e0d4ef94236 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java +++ b/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -249,6 +249,7 @@ void waitForDeath() { acquireShutdownSignal(); + wrappedExecutorService.shutdown(); stopAllWatchers(); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/work/Engine.java b/operator/src/main/java/oracle/kubernetes/operator/work/Engine.java index 00c4911a5ba..2ad830ba3eb 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/work/Engine.java +++ b/operator/src/main/java/oracle/kubernetes/operator/work/Engine.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.work; @@ -48,7 +48,12 @@ public Engine() { * @return executor service */ public static ScheduledExecutorService wrappedExecutorService(Container container) { - ScheduledThreadPoolExecutor threadPool = new ScheduledThreadPoolExecutor(DEFAULT_THREAD_COUNT); + ScheduledThreadPoolExecutor threadPool = new ScheduledThreadPoolExecutor( + DEFAULT_THREAD_COUNT, r -> { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setDaemon(true); + return t; + }); threadPool.setRemoveOnCancelPolicy(true); return wrap(container, new VirtualScheduledExectuorService(threadPool)); } diff --git a/operator/src/test/java/oracle/kubernetes/operator/work/FiberTestSupport.java b/operator/src/test/java/oracle/kubernetes/operator/work/FiberTestSupport.java index 74623bed150..7e1f37a6944 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/work/FiberTestSupport.java +++ b/operator/src/test/java/oracle/kubernetes/operator/work/FiberTestSupport.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.work; @@ -284,6 +284,11 @@ int getNumItemsRun() { return numItemsRun; } + @Override + public void shutdown() { + // no-op + } + @Override @Nonnull public ScheduledFuture schedule( From 2ba28a26035343b88a35c9368f5588d288722ea2 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Tue, 10 Sep 2024 22:22:36 +0000 Subject: [PATCH 157/356] Moved some tests to OKE parallel --- .../weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java | 2 +- .../test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java | 2 +- .../oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java | 2 +- .../java/oracle/weblogic/kubernetes/ItManagedCoherence.java | 2 +- .../java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java index a16d3a75a8a..01560558930 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItAddNewDynamicClusterUsingWlst.java @@ -57,7 +57,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-parallel") class ItAddNewDynamicClusterUsingWlst { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java index f3326916693..42868802009 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java @@ -68,7 +68,7 @@ */ @DisplayName("Test Operator and WebLogic domain with Dedicated set to true") @Tag("kind-sequential") -@Tag("oke-gate") +@Tag("oke-parallel") @Tag("okd-wls-mrg") @IntegrationTest class ItDedicatedMode { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java index 1293a37e8d7..058996ce249 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java @@ -78,7 +78,7 @@ @DisplayName("Test the monitoring WebLogic Domain via istio provided Prometheus") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-parallel") @Tag("kind-parallel") @Tag("olcne-mrg") class ItIstioMonitoringExporter { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java index 1115bb95ad3..cb98c27250c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManagedCoherence.java @@ -78,7 +78,7 @@ @IntegrationTest @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-gate") +@Tag("oke-parallel") class ItManagedCoherence { // constants for Coherence diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index f0693001067..4a8849a0311 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -121,7 +121,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-gate") +@Tag("oke-parallel") class ItMiiAuxiliaryImage { private static String domainNamespace = null; From aa1dd7f1c3a9c4376dab9702ae7f9af22a485e0f Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 11 Sep 2024 09:56:15 -0400 Subject: [PATCH 158/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index df269675527..0baccbbf6f9 100644 --- a/pom.xml +++ b/pom.xml @@ -722,7 +722,7 @@ 2.11.0 10.0.4 2.0.16 - 1.5.7 + 1.5.8 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From d72418e63e239a05d2e87448c58780de0dd08ca9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 11 Sep 2024 10:01:22 -0400 Subject: [PATCH 159/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 91e6db65f6c..ed13b25f890 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index d84a4957bfb..b88e34e8a63 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 12ce863844c..a58fd080777 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index c2ca6be445b..73548981990 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.6 + 4.2.7-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 22a1081f167..82ccb45ba29 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 2a80271b476..a180c100593 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.6 + 4.2.7-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 8c9dddebdb6..46a35b55bc9 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 0baccbbf6f9..d932b34fb80 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 5d065e93870..355d954ebf2 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.6 + 4.2.7-SNAPSHOT operator-swagger From 7fc98761bce64b0ca87482fb236b52006372f432 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 11 Sep 2024 11:14:16 -0400 Subject: [PATCH 160/356] Prepare for release 4.2.7 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index ed13b25f890..ab429a1e786 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index b88e34e8a63..0465a64fb4f 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index a58fd080777..2d1022a46d3 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 73548981990..771f61112fa 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.7-SNAPSHOT + 4.2.7 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 82ccb45ba29..c035b3de19a 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index a180c100593..051e3f49632 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.7-SNAPSHOT + 4.2.7 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 46a35b55bc9..ed160141f62 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index d932b34fb80..6265f28f17d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 355d954ebf2..61a858c7c98 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7-SNAPSHOT + 4.2.7 operator-swagger From 7394a56ef6ac3231a766455d8d199095f6e69fbb Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 11 Sep 2024 12:44:09 -0400 Subject: [PATCH 161/356] Restore usage of PUBLISH_SECRET for pushing container image --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 26438b34973..5c076c2f251 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.PUBLISH_SECRET }} - name: Build and push container image uses: docker/build-push-action@v6 From 7611b0234ec5db6327032336147fd5361b1a38b8 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 11 Sep 2024 13:42:47 -0400 Subject: [PATCH 162/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index ab429a1e786..d6d9974e37f 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 0465a64fb4f..2080ff8796a 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 2d1022a46d3..45f6ca2befd 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 771f61112fa..486355cbd48 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.7 + 4.2.8-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index c035b3de19a..d960507625b 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 051e3f49632..ed665bc21cc 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.7 + 4.2.8-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index ed160141f62..79851240e60 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 6265f28f17d..8cd48438fcd 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 61a858c7c98..c12b7f22689 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.7 + 4.2.8-SNAPSHOT operator-swagger From 4819c3c3a16f445c0aefdfde5dcfcf31b6864856 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 11 Sep 2024 18:25:09 +0000 Subject: [PATCH 163/356] Removed ItDedicatedMode test from parallel --- .../test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java index 42868802009..f3326916693 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java @@ -68,7 +68,7 @@ */ @DisplayName("Test Operator and WebLogic domain with Dedicated set to true") @Tag("kind-sequential") -@Tag("oke-parallel") +@Tag("oke-gate") @Tag("okd-wls-mrg") @IntegrationTest class ItDedicatedMode { From 7740b40c20d0d0203b57b0191722c64b35cc8fdc Mon Sep 17 00:00:00 2001 From: maggie_he Date: Wed, 11 Sep 2024 19:15:15 +0000 Subject: [PATCH 164/356] Cross domain distributed transaction using CrossDomainSecurity inside k8s --- .../ItCrossDomainTransactionSecurity.java | 528 ++++++++++++++++++ .../crossdomain-security/META-INF/MANIFEST.MF | 3 + .../META-INF/application.xml | 15 + .../META-INF/weblogic-application.xml | 7 + .../sample_war/WEB-INF/web.xml | 9 + .../sample_war/WEB-INF/weblogic.xml | 15 + .../crossdomain-security/sample_war/dtx.jsp | 98 ++++ .../crossdomain-security/sample_war/get.jsp | 80 +++ .../crossdomsecurity/model.dynamic.wls.yaml | 54 ++ .../crossdomsecurity/sparse.application.yaml | 8 + .../crossdomsecurity/sparse.jdbc.yaml | 22 + .../crossdomsecurity/sparse.jms.yaml | 64 +++ 12 files changed, 903 insertions(+) create mode 100644 integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java create mode 100755 integration-tests/src/test/resources/apps/crossdomain-security/META-INF/MANIFEST.MF create mode 100755 integration-tests/src/test/resources/apps/crossdomain-security/META-INF/application.xml create mode 100755 integration-tests/src/test/resources/apps/crossdomain-security/META-INF/weblogic-application.xml create mode 100755 integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/web.xml create mode 100755 integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/weblogic.xml create mode 100644 integration-tests/src/test/resources/apps/crossdomain-security/sample_war/dtx.jsp create mode 100644 integration-tests/src/test/resources/apps/crossdomain-security/sample_war/get.jsp create mode 100755 integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml create mode 100755 integration-tests/src/test/resources/crossdomsecurity/sparse.application.yaml create mode 100755 integration-tests/src/test/resources/crossdomsecurity/sparse.jdbc.yaml create mode 100755 integration-tests/src/test/resources/crossdomsecurity/sparse.jms.yaml diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java new file mode 100644 index 00000000000..9764a35982b --- /dev/null +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java @@ -0,0 +1,528 @@ +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.weblogic.kubernetes; + +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import oracle.weblogic.domain.AuxiliaryImage; +import oracle.weblogic.domain.Channel; +import oracle.weblogic.domain.ClusterList; +import oracle.weblogic.domain.ClusterSpec; +import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.kubernetes.actions.impl.AppParams; +import oracle.weblogic.kubernetes.actions.impl.Cluster; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; +import oracle.weblogic.kubernetes.annotations.IntegrationTest; +import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecCommand; +import oracle.weblogic.kubernetes.utils.ExecResult; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static java.net.InetAddress.getLocalHost; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOSTNAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; +import static oracle.weblogic.kubernetes.actions.TestActions.buildAppArchive; +import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; +import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runClientInsidePod; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runJavacInsidePod; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; +import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretsForImageRepos; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Cross domain transaction with CrossDomainSecurityEnabled set to true. + */ +@DisplayName("Verify cross domain transaction is successful with CrossDomainSecurityEnabled set to true") +@IntegrationTest +@Tag("kind-parallel") +class ItCrossDomainTransactionSecurity { + + private static final String auxImageName1 = DOMAIN_IMAGES_PREFIX + "domain1-cdxaction-aux"; + private static final String auxImageName2 = DOMAIN_IMAGES_PREFIX + "domain2-cdxaction-aux"; + private static final String PROPS_TEMP_DIR = RESULTS_ROOT + "/crossdomsecurity"; + + + private static String opNamespace = null; + private static String domainNamespace = null; + private static String domainUid1 = "domain1"; + private static String domainUid2 = "domain2"; + private static String adminServerName = "admin-server"; + private static String domain1AdminServerPodName = domainUid1 + "-" + adminServerName; + private static String domain1ManagedServerPrefix = domainUid1 + "-managed-server"; + private static String domain2AdminServerPodName = domainUid2 + "-" + adminServerName; + private static String domain2ManagedServerPrefix = domainUid2 + "-managed-server"; + private static LoggingFacade logger = null; + private static int replicaCount = 2; + private static int t3ChannelPort1 = getNextFreePort(); + private static int t3ChannelPort2 = getNextFreePort(); + private static String domain1AdminExtSvcRouteHost = null; + private static String hostAndPort1 = null; + private static String hostHeader1; + private static Map headers = null; + + + + /** + * Install Operator. + * @param namespaces list of namespaces created by the IntegrationTestWatcher by the + * JUnit engine parameter resolution mechanism + */ + @BeforeAll + public static void initAll(@Namespaces(3) List namespaces) { + logger = getLogger(); + + // get a new unique opNamespace + logger.info("Creating unique namespace for Operator"); + assertNotNull(namespaces.get(0), "Namespace list is null"); + opNamespace = namespaces.get(0); + + logger.info("Creating unique namespace for Domain"); + assertNotNull(namespaces.get(1), "Namespace list is null"); + domainNamespace = namespaces.get(1); + + // Create the repo secret to pull the image + // this secret is used only for non-kind cluster + createTestRepoSecret(domainNamespace); + + // install and verify operator + installAndVerifyOperator(opNamespace, domainNamespace); + buildDomains(); + + } + + /** + * Configure two domains d1 and d2 with CrossDomainSecurityEnabled set to true + * On both domains create a user (cross-domain) with group CrossDomainConnectors + * Add required Credential Mapping + * Deploy a JSP on d1's admin server that takes 2 parameteers + * a. The tx aaction b. the d2's cluster service url + * Starts a User transcation + * Send 10 messgaes to a distributed destination (jms.testUniformQueue) on d2 that has 2 members + * Send a message to local destination (jms.admin.adminQueue) on d1 + * Commit/rollback the transation + * Receive the messages from the distributed destination (jms.testUniformQueue) on d2 + * Receive the message from the local destination (jms.admin.adminQueue) on d1 + */ + @Test + @DisplayName("Check cross domain transaction works") + void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostException { + + logger.info("2 domains with crossDomainSecurity enabled start up!"); + int domain1AdminServiceNodePort + = getServiceNodePort(domainNamespace, getExternalServicePodName(domain1AdminServerPodName), "default"); + assertNotEquals(-1, domain1AdminServiceNodePort, "domain1 admin server default node port is not valid"); + logger.info("domain1AdminServiceNodePort is: " + domain1AdminServiceNodePort); + int domain2AdminServiceNodePort + = getServiceNodePort(domainNamespace, getExternalServicePodName(domain2AdminServerPodName), "default"); + assertNotEquals(-1, domain1AdminServiceNodePort, "domain2 admin server default node port is not valid"); + logger.info("domain2AdminServiceNodePort is: " + domain2AdminServiceNodePort); + + hostAndPort1 = getHostAndPort(domain1AdminExtSvcRouteHost, domain1AdminServiceNodePort); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + hostHeader1 = createIngressHostRouting(domainNamespace, domainUid1, adminServerName, 7001); + hostAndPort1 = formatIPv6Host(getLocalHost().getHostAddress()) + + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT; + + } + logger.info("hostHeader1 for domain1 is: " + hostHeader1); + logger.info("hostAndPort1 for domain1 is: " + hostAndPort1); + + // build the standalone JMS Client on Admin pod + String destLocation = "/u01/JmsSendReceiveClient.java"; + assertDoesNotThrow(() -> copyFileToPod(domainNamespace, + domain1AdminServerPodName, "", + Paths.get(RESOURCE_DIR, "jms", "JmsSendReceiveClient.java"), + Paths.get(destLocation))); + runJavacInsidePod(domain1AdminServerPodName, domainNamespace, destLocation); + + //In a UserTransaction send 10 msg to remote udq and 1 msg to local queue and commit the tx + StringBuffer curlCmd1 = new StringBuffer("curl -skg --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd1.append(" -H 'host: " + hostHeader1 + "' "); + } + String url1 = "\"http://" + hostAndPort1 + + "/sample_war/dtx.jsp?remoteurl=t3://domain2-cluster-cluster-2:8001&action=commit\""; + curlCmd1.append(url1); + logger.info("Executing curl command: {0}", curlCmd1); + assertTrue(getCurlResult(curlCmd1.toString()).contains("Message sent in a commit User Transation"), + "Didn't send expected msg "); + + //receive msg from the udq that has 2 memebers + StringBuffer curlCmd2 = new StringBuffer("curl -j --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd2.append(" -H 'host: " + hostHeader1 + "' "); + } + String url2 = "\"http://" + hostAndPort1 + + "/sample_war/get.jsp?remoteurl=t3://domain2-cluster-cluster-2:8001&action=recv&dest=jms.testUniformQueue\""; + curlCmd2.append(url2); + logger.info("Executing curl command: {0}", curlCmd2); + for (int i = 0; i < 2; i++) { + assertTrue(getCurlResult(curlCmd2.toString()).contains("Total Message(s) Received : 5"), + "Didn't receive expected msg count from remote queue"); + } + + // receive 1 msg from the local queue + testUntil( + runClientInsidePod(domain1AdminServerPodName, domainNamespace, + "/u01", "JmsSendReceiveClient", + "t3://" + K8S_NODEPORT_HOST + ":" + t3ChannelPort1, "receive", "jms.admin.adminQueue", "1"), + logger, + "Wait for JMS Client to send/recv msg"); + + //In a UserTransaction send 10 msg to remote udq and 1 msg to local queue and rollback the tx + StringBuffer curlCmd3 = new StringBuffer("curl -skg --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd3.append(" -H 'host: " + hostHeader1 + "' "); + } + String url3 = "\"http://" + hostAndPort1 + + "/sample_war/dtx.jsp?remoteurl=t3://domain2-cluster-cluster-2:8001&action=rollback\""; + curlCmd3.append(url3); + logger.info("Executing curl command: {0}", curlCmd3); + assertTrue(getCurlResult(curlCmd3.toString()).contains("Message sent in a rolled-back User Transation"), + "Didn't send expected msg "); + + //receive 0 msg from the udq that has 2 memebers + StringBuffer curlCmd4 = new StringBuffer("curl -j --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd4.append(" -H 'host: " + hostHeader1 + "' "); + } + String url4 = "\"http://" + hostAndPort1 + + "/sample_war/get.jsp?remoteurl=t3://domain2-cluster-cluster-2:8001&action=recv&dest=jms.testUniformQueue\""; + curlCmd4.append(url4); + logger.info("Executing curl command: {0}", curlCmd4); + for (int i = 0; i < 2; i++) { + assertTrue(getCurlResult(curlCmd4.toString()).contains("Total Message(s) Received : 0"), + "Didn't receive expected msg count from remote queue"); + } + + // receive 0 msg from the local queue + testUntil( + runClientInsidePod(domain1AdminServerPodName, domainNamespace, + "/u01", "JmsSendReceiveClient", + "t3://" + K8S_NODEPORT_HOST + ":" + t3ChannelPort1, "receive", "jms.admin.adminQueue", "0"), + logger, + "Wait for JMS Client to send/recv msg"); + } + + private static String createAuxImage(String imageName, String imageTag, List wdtModelFile, + String wdtVariableFile) { + + // build sample-app application + AppParams appParams = defaultAppParams() + .srcDirList(Collections.singletonList("crossdomain-security")) + .appArchiveDir(ARCHIVE_DIR + ItCrossDomainTransactionSecurity.class.getName()) + .appName("crossdomainsec"); + assertTrue(buildAppArchive(appParams), + String.format("Failed to create app archive for %s", "crossdomainsec")); + List archiveList = Collections.singletonList(appParams.appArchiveDir() + "/" + "crossdomainsec" + ".zip"); + + //create an auxiliary image with model and application + WitParams witParams + = new WitParams() + .modelImageName(imageName) + .modelImageTag(imageTag) + .modelFiles(wdtModelFile) + .modelVariableFiles(Arrays.asList(wdtVariableFile)) + .modelArchiveFiles(archiveList); + createAndPushAuxiliaryImage(imageName, imageTag, witParams); + + return imageName + ":" + imageTag; + } + + private static void buildDomains() { + + String auxImageTag = getDateAndTimeStamp(); + String modelDir = RESOURCE_DIR + "/" + "crossdomsecurity"; + List modelList = new ArrayList<>(); + modelList.add(modelDir + "/" + "model.dynamic.wls.yaml"); + modelList.add(modelDir + "/sparse.jdbc.yaml"); + modelList.add(modelDir + "/sparse.jms.yaml"); + modelList.add(modelDir + "/sparse.application.yaml"); + + // create WDT properties file for the WDT model domain1 + Path wdtVariableFile1 = Paths.get(WORK_DIR, ItCrossDomainTransactionSecurity.class.getName(), + "wdtVariable1.properties"); + logger.info("The K8S_NODEPORT_HOSTNAME is: " + K8S_NODEPORT_HOSTNAME); + logger.info("The K8S_NODEPORT_HOST is: " + K8S_NODEPORT_HOST); + logger.info("In the domain1 t3ChannelPort1 is: " + t3ChannelPort1); + logger.info("In the domain2 t3ChannelPort2 is " + t3ChannelPort2); + + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile1); + Files.createDirectories(wdtVariableFile1.getParent()); + Files.writeString(wdtVariableFile1, "DOMAIN_UID=domain1\n", StandardOpenOption.CREATE); + Files.writeString(wdtVariableFile1, "CLUSTER_NAME=cluster-1\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "ADMIN_SERVER_NAME=admin-server\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "MANAGED_SERVER_BASE_NAME=managed-server\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "MANAGED_SERVER_PORT=8001\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "MANAGED_SERVER_COUNT=4\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "T3PUBLICADDRESS=" + K8S_NODEPORT_HOSTNAME + "\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "T3CHANNELPORT=" + t3ChannelPort1 + "\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile1, "REMOTE_DOMAIN=domain2\n", StandardOpenOption.APPEND); + }); + + // create auxiliary image for domain1 + String auxImage1 = createAuxImage(auxImageName1, auxImageTag, modelList, wdtVariableFile1.toString()); + + // create WDT properties file for the WDT model domain2 + Path wdtVariableFile2 = Paths.get(WORK_DIR, ItCrossDomainTransactionSecurity.class.getName(), + "wdtVariable2.properties"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile2); + Files.createDirectories(wdtVariableFile2.getParent()); + Files.writeString(wdtVariableFile2, "DOMAIN_UID=domain2\n", StandardOpenOption.CREATE); + Files.writeString(wdtVariableFile2, "CLUSTER_NAME=cluster-2\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "ADMIN_SERVER_NAME=admin-server\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "MANAGED_SERVER_BASE_NAME=managed-server\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "MANAGED_SERVER_PORT=8001\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "MANAGED_SERVER_COUNT=4\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "T3PUBLICADDRESS=" + K8S_NODEPORT_HOSTNAME + "\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "T3CHANNELPORT=" + t3ChannelPort2 + "\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile2, "REMOTE_DOMAIN=domain1\n", StandardOpenOption.APPEND); + }); + + // create auxiliary image for domain2 + String auxImage2 = createAuxImage(auxImageName2, auxImageTag, modelList, wdtVariableFile2.toString()); + + // create admin credential secret for domain1 + logger.info("Create admin credential secret for domain1"); + String domain1AdminSecretName = domainUid1 + "-weblogic-credentials"; + assertDoesNotThrow(() -> createSecretWithUsernamePassword( + domain1AdminSecretName, domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), + String.format("createSecret %s failed for %s", domain1AdminSecretName, domainUid1)); + + // create admin credential secret for domain2 + logger.info("Create admin credential secret for domain2"); + String domain2AdminSecretName = domainUid2 + "-weblogic-credentials"; + assertDoesNotThrow(() -> createSecretWithUsernamePassword( + domain2AdminSecretName, domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), + String.format("createSecret %s failed for %s", domain2AdminSecretName, domainUid2)); + + // create encryption secret + logger.info("Create encryption secret"); + String encryptionSecretName = "encryptionsecret"; + createSecretWithUsernamePassword(encryptionSecretName, domainNamespace, + "weblogicenc", "weblogicenc"); + + //create domain1 and verify its running + createDomain(domainUid1, auxImage1, domainNamespace, domain1AdminSecretName, encryptionSecretName, + "cluster-1", domain1AdminServerPodName, domain1ManagedServerPrefix, t3ChannelPort1); + + //create domain2 and verify its running + createDomain(domainUid2, auxImage2, domainNamespace, domain2AdminSecretName, encryptionSecretName, + "cluster-2", domain2AdminServerPodName, domain2ManagedServerPrefix, t3ChannelPort2); + } + + private static void createDomain(String domainUid, String imageName, String domainNamespace, String + domainAdminSecretName, String encryptionSecretName, String clusterName, String adminServerPodName, + String managedServerPrefix, int t3ChannelPort) { + + final String auxiliaryImagePath = "/auxiliary"; + //create domain resource with the auxiliary image + logger.info("Creating domain custom resource with domainUid {0} and auxiliary images {1}", + domainUid, imageName); + DomainResource domainCR = createDomainResourceWithAuxiliaryImage(domainUid, domainNamespace, + WEBLOGIC_IMAGE_TO_USE_IN_SPEC, domainAdminSecretName, createSecretsForImageRepos(domainNamespace), + encryptionSecretName, t3ChannelPort, auxiliaryImagePath, + imageName); + + domainCR = createClusterResourceAndAddReferenceToDomain( + domainUid + "-" + clusterName, clusterName, domainNamespace, domainCR, replicaCount); + // create domain and verify its running + logger.info("Creating domain {0} with auxiliary images {1} {2} in namespace {3}", + domainUid, imageName, domainNamespace); + createDomainAndVerify(domainUid, domainCR, domainNamespace, + adminServerPodName, managedServerPrefix, replicaCount); + + } + + private static DomainResource createDomainResourceWithAuxiliaryImage( + String domainResourceName, + String domNamespace, + String baseImageName, + String adminSecretName, + String[] repoSecretName, + String encryptionSecretName, + int t3ChannelPort, + String auxiliaryImagePath, + String... auxiliaryImageName) { + + DomainResource domainCR = createDomainResource( + domainResourceName, + domNamespace, + baseImageName, + adminSecretName, + repoSecretName, + encryptionSecretName, + replicaCount, + Collections.emptyList(), + false, + 0, + t3ChannelPort); + int index = 0; + for (String cmImageName: auxiliaryImageName) { + AuxiliaryImage auxImage = new AuxiliaryImage() + .image(cmImageName).imagePullPolicy(IMAGE_PULL_POLICY); + //Only add the sourceWDTInstallHome and sourceModelHome for the first aux image. + if (index == 0) { + auxImage.sourceWDTInstallHome(auxiliaryImagePath + "/weblogic-deploy") + .sourceModelHome(auxiliaryImagePath + "/models"); + } + domainCR.spec().configuration().model().withAuxiliaryImage(auxImage); + index++; + } + return domainCR; + } + + private static DomainResource createDomainResource( + String domainResourceName, + String domNamespace, + String imageName, + String adminSecretName, + String[] repoSecretName, + String encryptionSecretName, + int replicaCount, + List clusterNames, + boolean prefixDomainName, + int nodePort, + int t3ChannelPort) { + + // create secrets + List secrets = new ArrayList<>(); + for (String secret : repoSecretName) { + secrets.add(new V1LocalObjectReference().name(secret)); + } + + // create the domain CR + DomainResource domain = new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new io.kubernetes.client.openapi.models.V1ObjectMeta() + .name(domainResourceName) + .namespace(domNamespace)) + .spec(new oracle.weblogic.domain.DomainSpec() + .domainUid(domainResourceName) + .domainHomeSourceType("FromModel") + .image(imageName) + .imagePullPolicy(IMAGE_PULL_POLICY) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(adminSecretName)) + .includeServerOutInPodLog(true) + .serverStartPolicy("IfNeeded") + .serverPod(new oracle.weblogic.domain.ServerPod() + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true")) + .addEnvItem(new io.kubernetes.client.openapi.models.V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false")) + .addEnvItem(new io.kubernetes.client.openapi.models.V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom "))) + .adminServer(new oracle.weblogic.domain.AdminServer() + .adminService(new oracle.weblogic.domain.AdminService() + .addChannelsItem(new oracle.weblogic.domain.Channel() + .channelName("default") + .nodePort(nodePort)) + .addChannelsItem(new Channel() + .channelName("T3Channel") + .nodePort(t3ChannelPort)))) + .configuration(new oracle.weblogic.domain.Configuration() + .model(new oracle.weblogic.domain.Model() + .domainType("WLS") + .runtimeEncryptionSecret(encryptionSecretName)) + .introspectorJobActiveDeadlineSeconds(3000L))); + + domain.spec().setImagePullSecrets(secrets); + + ClusterList clusters = Cluster.listClusterCustomResources(domNamespace); + + if (clusterNames != null) { + for (String clusterName : clusterNames) { + String clusterResName = prefixDomainName ? domainResourceName + "-" + clusterName : clusterName; + if (clusters.getItems().stream().anyMatch(cluster -> cluster.getClusterName().equals(clusterResName))) { + getLogger().info("!!!Cluster {0} in namespace {1} already exists, skipping...", clusterResName, domNamespace); + } else { + getLogger().info("Creating cluster {0} in namespace {1}", clusterResName, domNamespace); + ClusterSpec spec = + new ClusterSpec().withClusterName(clusterName).replicas(replicaCount).serverStartPolicy("IfNeeded"); + createClusterAndVerify(createClusterResource(clusterResName, domNamespace, spec)); + } + // set cluster references + domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterResName)); + } + } + + setPodAntiAffinity(domain); + return domain; + } + + private String getCurlResult(String curlCmd) { + ExecResult result = null; + try { + result = ExecCommand.exec(curlCmd, true); + } catch (Exception e) { + logger.info("Got exception while running command: {0}", curlCmd); + logger.info(e.toString()); + } + if (result != null) { + logger.info("result.stderr: \n{0}", result.stderr()); + } + return result.stdout(); + } + +} + diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/MANIFEST.MF b/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/MANIFEST.MF new file mode 100755 index 00000000000..8a636006c23 --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Created-By: 1.8.0_201 (Oracle Corporation) + diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/application.xml b/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/application.xml new file mode 100755 index 00000000000..923bbc4f289 --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/application.xml @@ -0,0 +1,15 @@ + + + + JSP 2.0 Expression Language Example + JSP 2.0 Expression Language Example + + + sample_war + sample_war + + + diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/weblogic-application.xml b/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/weblogic-application.xml new file mode 100755 index 00000000000..6d432babfb0 --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/META-INF/weblogic-application.xml @@ -0,0 +1,7 @@ + + + + diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/web.xml b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/web.xml new file mode 100755 index 00000000000..8acacb93faf --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/web.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/weblogic.xml b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/weblogic.xml new file mode 100755 index 00000000000..e278c8295f2 --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/WEB-INF/weblogic.xml @@ -0,0 +1,15 @@ + + + + + 15 + 60 + + + 1 + true + + diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/dtx.jsp b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/dtx.jsp new file mode 100644 index 00000000000..6754bcb4774 --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/dtx.jsp @@ -0,0 +1,98 @@ + +<%@ page import="java.io.IOException" %> +<%@ page import="java.util.Hashtable" %> +<%@ page import="javax.naming.Context" %> +<%@ page import="javax.naming.InitialContext" %> +<%@ page import="javax.naming.NamingException" %> +<%@ page import="javax.servlet.ServletException" %> +<%@ page import="javax.servlet.annotation.WebServlet" %> +<%@ page import="javax.servlet.http.HttpServlet" %> +<%@ page import="javax.servlet.http.HttpServletRequest" %> +<%@ page import="javax.servlet.http.HttpServletResponse" %> +<%@ page import="javax.servlet.http.HttpServletResponse" %> +<%@ page import="javax.jms.Destination" %> +<%@ page import="javax.jms.ConnectionFactory" %> +<%@ page import="javax.jms.JMSContext" %> +<%@ page import="javax.jms.Message" %> +<%@ page import="javax.jms.JMSConsumer" %> +<%@ page import="javax.jms.QueueBrowser" %> +<%@ page import="weblogic.transaction.TransactionHelper" %> +<%@ page import="weblogic.transaction.TransactionManager" %> +<%@ page import="javax.transaction.UserTransaction" %> + +<% +try { + Context lctx = null; + Context rctx = null; + String remoteurl = request.getParameter("remoteurl"); + out.println("#### Remote URL is ["+remoteurl+"]"); + String action = request.getParameter("action"); + out.println("#### Transcation action ["+action+"]"); + + lctx = new InitialContext(); + out.println("(Local) Got JNDI Context successfully ["+lctx+"]"); + TransactionHelper tranhelp =TransactionHelper.getTransactionHelper(); + UserTransaction ut = tranhelp.getUserTransaction(); + + ConnectionFactory qcf= + (ConnectionFactory)lctx.lookup("weblogic.jms.XAConnectionFactory"); + out.println("(Local) JMS ConnectionFactory lookup successful ..."); + JMSContext context = qcf.createContext(); + out.println("(Local) JMS Context created successfully ..."); + Destination queue = (Destination)lctx.lookup("jms.admin.adminQueue"); + out.println("(Local) JMS Destination (jms.admin.adminQueue) lookup successful ..."); + + if ( ! action.equals("notx") ) { + out.println("Started a user transaction"); + ut.begin(); + } + + // Send message to local Destination + context.createProducer().send(queue, "Message to a Local Destination"); + lctx.close(); + + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "weblogic.jndi.WLInitialContextFactory"); + env.put(Context.PROVIDER_URL, remoteurl); + // Remote anonymous RMI access via T3 not allowed + env.put(Context.SECURITY_PRINCIPAL, "weblogic"); + env.put(Context.SECURITY_CREDENTIALS, "welcome1"); + rctx = new InitialContext(env); + out.println("(Remote) Got JNDI Context successfully ["+rctx+"]"); + + // lookup JMS XAConnectionFactory + ConnectionFactory qcf2= + (ConnectionFactory)rctx.lookup("jms/ClusterConnectionFactory"); + out.println("(Remote) JMS ConnectionFactory lookup successful"); + + JMSContext context2 = qcf2.createContext(); + out.println("(Remote) JMS Context created successfully"); + Destination queue2 = (Destination)rctx.lookup("jms.testUniformQueue"); + out.println("(Remote) JMS Destination (jms.testUniformQueue) lookup successful "); + + for (int i=0; i<10; i++) + context2.createProducer().send(queue2, "Message to a Remote Destination"); + rctx.close(); + + // Get the live context from Tx Coordinator before closing transaction + // Context ctx = new InitialContext(env); + + if ( action.equals("commit") ) { + out.println(ut); + ut.commit(); + out.println("#### Message sent in a commit User Transation"); + } else if ( action.equals("rollback")) { + out.println(ut); + ut.rollback(); + out.println("#### Message sent in a rolled-back User Transation"); + } else { + out.println("#### Message sent w/o Transaction"); + } +} catch(Exception e) { + out.println("#### Got an Exception [" +e+"]"); +} +%> diff --git a/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/get.jsp b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/get.jsp new file mode 100644 index 00000000000..28150da9ae6 --- /dev/null +++ b/integration-tests/src/test/resources/apps/crossdomain-security/sample_war/get.jsp @@ -0,0 +1,80 @@ + +<%@ page import="java.io.IOException" %> +<%@ page import="java.util.Hashtable" %> +<%@ page import="javax.naming.Context" %> +<%@ page import="javax.naming.InitialContext" %> +<%@ page import="javax.naming.NamingException" %> +<%@ page import="javax.servlet.ServletException" %> +<%@ page import="javax.servlet.annotation.WebServlet" %> +<%@ page import="javax.servlet.http.HttpServlet" %> +<%@ page import="javax.servlet.http.HttpServletRequest" %> +<%@ page import="javax.servlet.http.HttpServletResponse" %> +<%@ page import="javax.servlet.http.HttpServletResponse" %> +<%@ page import="javax.jms.Destination" %> +<%@ page import="javax.jms.ConnectionFactory" %> +<%@ page import="javax.jms.JMSContext" %> +<%@ page import="javax.jms.Message" %> +<%@ page import="javax.jms.JMSConsumer" %> +<%@ page import="javax.jms.QueueBrowser" %> + +<% +try { + Context ctx = null; + + String remoteurl = request.getParameter("remoteurl"); + out.println("Remote URL is [" + remoteurl + "]"); + + String action = request.getParameter("action"); + out.println("action [" + action + "]"); + + String dest = request.getParameter("dest"); + out.println("Destination [" + dest + "]"); + + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "weblogic.jndi.WLInitialContextFactory"); + env.put(Context.PROVIDER_URL, remoteurl); + // Remote anonymous RMI access via T3 not allowed + env.put(Context.SECURITY_PRINCIPAL, "weblogic"); + env.put(Context.SECURITY_CREDENTIALS, "welcome1"); + ctx = new InitialContext(env); + out.println("Got Remote Context successfully"); + + // lookup JMS XAConnectionFactory + ConnectionFactory qcf= + (ConnectionFactory)ctx.lookup("weblogic.jms.XAConnectionFactory"); + out.println("JMS ConnectionFactory lookup Successful ..."); + + JMSContext context = qcf.createContext(); + out.println("JMS Context Created Successfully ..."); + Destination queue = (Destination)ctx.lookup(dest); + out.println("JMS Destination lookup Successful ..."); + + if ( action.equals("send") ) { + context.createProducer().send(queue, "Message to a Destination"); + out.println("Message sent to the JMS Destination"); + } + + if ( action.equals("recv") ) { + JMSConsumer consumer = (JMSConsumer) context.createConsumer(queue); + out.println("JMS Consumer Created Successfully .."); + Message msg=null; + int count = 0; + do { + msg = consumer.receiveNoWait(); + if ( msg != null ) { + // out.println("Message Drained ["+msg+"]"); + // out.println("Message Drained ["+msg.getBody(String.class)+"]"); + count++; + } + } while( msg != null); + out.println("Total Message(s) Received : " + count); + } + +} catch(Exception e) { + out.println("Got an Exception [" + e + "]"); +} +%> diff --git a/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml b/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml new file mode 100755 index 00000000000..f4a4442b66a --- /dev/null +++ b/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2024, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'prod' + WLSUserPasswordCredentialMappings: + CrossDomain: + map1: + RemoteDomain: '@@PROP:REMOTE_DOMAIN@@' + RemoteUser: xdomain + RemotePassword: '@@SECRET:__weblogic-credentials__:password@@' + +topology: + Name: '@@PROP:DOMAIN_UID@@' + AdminServerName: "@@PROP:ADMIN_SERVER_NAME@@" + SecurityConfiguration: + CrossDomainSecurityEnabled: true + Security: + User: + xdomain: + Name: xdomain + Password: '@@SECRET:__weblogic-credentials__:password@@' + GroupMemberOf: + - CrossDomainConnectors + Cluster: + "@@PROP:CLUSTER_NAME@@": + DynamicServers: + ServerTemplate: "@@PROP:CLUSTER_NAME@@-template" + ServerNamePrefix: "@@PROP:MANAGED_SERVER_BASE_NAME@@" + DynamicClusterSize: "@@PROP:MANAGED_SERVER_COUNT@@" + MaxDynamicClusterSize: "@@PROP:MANAGED_SERVER_COUNT@@" + CalculatedListenPorts: false + Server: + "@@PROP:ADMIN_SERVER_NAME@@": + ListenPort: 7001 + NetworkAccessPoint: + T3Channel: + ListenPort: '@@PROP:T3CHANNELPORT@@' + PublicAddress: '@@PROP:T3PUBLICADDRESS@@' + PublicPort: '@@PROP:T3CHANNELPORT@@' + ServerTemplate: + "@@PROP:CLUSTER_NAME@@-template": + Cluster: "@@PROP:CLUSTER_NAME@@" + ListenPort : '@@PROP:MANAGED_SERVER_PORT@@' +resources: + WebAppContainer: + WeblogicPluginEnabled: true +appDeployments: + Application: + myear: + SourcePath: wlsdeploy/applications/crossdomainsec.ear + ModuleType: ear + Target: '@@PROP:CLUSTER_NAME@@,@@PROP:ADMIN_SERVER_NAME@@' \ No newline at end of file diff --git a/integration-tests/src/test/resources/crossdomsecurity/sparse.application.yaml b/integration-tests/src/test/resources/crossdomsecurity/sparse.application.yaml new file mode 100755 index 00000000000..adad14d173b --- /dev/null +++ b/integration-tests/src/test/resources/crossdomsecurity/sparse.application.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +appDeployments: + Application: + myear: + SourcePath: wlsdeploy/applications/crossdomainsec.ear + ModuleType: ear + Target: '@@PROP:CLUSTER_NAME@@,@@PROP:ADMIN_SERVER_NAME@@' diff --git a/integration-tests/src/test/resources/crossdomsecurity/sparse.jdbc.yaml b/integration-tests/src/test/resources/crossdomsecurity/sparse.jdbc.yaml new file mode 100755 index 00000000000..88308c14432 --- /dev/null +++ b/integration-tests/src/test/resources/crossdomsecurity/sparse.jdbc.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2024, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +resources: + JDBCSystemResource: + TestDataSource: + Target: '@@PROP:CLUSTER_NAME@@' + JdbcResource: + JDBCConnectionPoolParams: + InitialCapacity: 0 + MinCapacity: 0 + MaxCapacity: 15 + JDBCDataSourceParams: + GlobalTransactionsProtocol: OnePhaseCommit + RowPrefetchSize: 200 + JNDIName: jdbc/TestDataSource + JDBCDriverParams: + URL: 'jdbc:oracle:thin:@//xxx.xxx.x.xxx:1521/ORCLCDB' + PasswordEncrypted: 'j2ee' + DriverName: oracle.jdbc.OracleDriver + Properties: + user: + Value: j2ee diff --git a/integration-tests/src/test/resources/crossdomsecurity/sparse.jms.yaml b/integration-tests/src/test/resources/crossdomsecurity/sparse.jms.yaml new file mode 100755 index 00000000000..b0e793c654c --- /dev/null +++ b/integration-tests/src/test/resources/crossdomsecurity/sparse.jms.yaml @@ -0,0 +1,64 @@ +# Copyright (c) 2024, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +resources: + FileStore: + TestFileStore: + Target: '@@PROP:ADMIN_SERVER_NAME@@' + TestClusterFileStore: + Target: '@@PROP:CLUSTER_NAME@@' + JMSServer: + TestJmsServer: + ProductionPausedAtStartup: false + ConsumptionPausedAtStartup: false + Target: '@@PROP:ADMIN_SERVER_NAME@@' + PersistentStore: 'TestFileStore' + InsertionPausedAtStartup: false + MessageCompressionOptions: GZIP_DEFAULT_COMPRESSION + TestClusterJmsServer: + ProductionPausedAtStartup: false + ConsumptionPausedAtStartup: false + Target: '@@PROP:CLUSTER_NAME@@' + PersistentStore: 'TestClusterFileStore' + InsertionPausedAtStartup: false + MessageCompressionOptions: GZIP_DEFAULT_COMPRESSION + + JMSSystemResource: + TestClusterJmsModule: + Target: '@@PROP:CLUSTER_NAME@@' + SubDeployment: + TestClusterSubDeployment: + Target: TestClusterJmsServer + JmsResource: + ConnectionFactory: + ClusterConnectionFactory: + JNDIName: jms/ClusterConnectionFactory + DefaultTargetingEnabled: true + LoadBalancingParams: + ServerAffinityEnabled: false + LoadBalancingEnabled: true + TransactionParams: + XAConnectionFactoryEnabled: true + UniformDistributedQueue: + testUniformQueue: + SubDeploymentName: TestClusterSubDeployment + JNDIName: jms/testUniformQueue + UniformDistributedTopic: + testUniformTopic: + SubDeploymentName: TestClusterSubDeployment + ForwardingPolicy: Partitioned + JNDIName: jms/testUniformTopic + + TestJmsModule: + Target: '@@PROP:ADMIN_SERVER_NAME@@' + SubDeployment: + TestSubDeployment: + Target: TestJmsServer + JmsResource: + Queue: + testQueue: + SubDeploymentName: TestSubDeployment + JNDIName: jms/admin/adminQueue + Topic: + testTopic: + SubDeploymentName: TestSubDeployment + JNDIName: jms/admin/adminTopic From 2aeda7cfc8f67bc26da3cd5765b30fee8256d3dc Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Fri, 13 Sep 2024 00:27:42 +0000 Subject: [PATCH 165/356] Split OKE sequential run into 2 to decrease execution time --- Jenkinsfile.oke | 4 +++- .../oracle/weblogic/kubernetes/ItCrossDomainTransaction.java | 2 +- .../test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java | 2 +- .../java/oracle/weblogic/kubernetes/ItIntrospectVersion.java | 2 +- .../oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java | 2 +- .../java/oracle/weblogic/kubernetes/ItMiiClusterResource.java | 2 +- .../oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java | 2 +- .../oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java | 2 +- .../oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java | 2 +- .../oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java | 2 +- .../oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java | 2 +- 11 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 058d44f747a..ac562bbc6f3 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -6,6 +6,7 @@ CRON_SETTINGS_MAIN = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' CRON_SETTINGS_42 = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false + H 1 * * * % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-nightly' ? CRON_SETTINGS_MAIN : CRON_SETTINGS_42}" @@ -114,9 +115,10 @@ pipeline { ) choice(name: 'MAVEN_PROFILE_NAME', - description: 'Profile to use in mvn command to run the tests. Possible values are oke-gate,oke-parallel. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + description: 'Profile to use in mvn command to run the tests. Possible values are oke-gate,oke-parallel, oke-sequential. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', choices: [ 'oke-gate', + 'oke-sequential', 'oke-parallel' ] ) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index 5481f27ff63..2788d99b9b0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -112,7 +112,7 @@ */ @DisplayName("Verify cross domain transaction is successful") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("kind-parallel") @Tag("okd-wls-srg") class ItCrossDomainTransaction { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index 26f58a1becb..7e5dbc239a6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -95,7 +95,7 @@ @DisplayName("Test to create a FMW domain in persistent volume with new simplified feature") @IntegrationTest @Tag("kind-sequential") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("okd-fmw-cert") class ItFmwDomainOnPV { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index 009c296f4cf..05ee4cea5da 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -175,7 +175,7 @@ @Tag("olcne-srg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("oke-arm") class ItIntrospectVersion { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index 53767bd9f2b..bf2526beae0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -150,7 +150,7 @@ @Tag("oke-arm") @IntegrationTest @Tag("olcne-srg") -@Tag("oke-gate") +@Tag("oke-sequential") class ItKubernetesDomainEvents { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java index f3ee4ef6b26..1e0fc8b1e26 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiClusterResource.java @@ -104,7 +104,7 @@ @Tag("kind-parallel") @Tag("okd-wls-srg") @Tag("oke-arm") -@Tag("oke-gate") +@Tag("oke-sequential") class ItMiiClusterResource { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java index cc0e8f7d44d..1dd9b556cda 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java @@ -66,7 +66,7 @@ @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("oke-arm") @Tag("olcne-srg") class ItMiiDynamicUpdatePart1 { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java index 317c27bd945..11dd73ae817 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java @@ -65,7 +65,7 @@ @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("oke-arm") class ItMiiDynamicUpdatePart2 { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java index 9f1d054d161..4839151bb0e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java @@ -77,7 +77,7 @@ @DisplayName("Test dynamic updates to a model in image domain, part3") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-mrg") diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index ca6affcebc9..1d12ea40fee 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -119,7 +119,7 @@ @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-srg") -@Tag("oke-gate") +@Tag("oke-sequential") class ItMiiUpdateDomainConfig { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java index 48fd984f238..8a4a53a0d6b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java @@ -121,7 +121,7 @@ + "rolling restart behavior in a multi-cluster MII domain and " + "the sample application can be accessed via NGINX ingress controller") @Tag("kind-sequential") -@Tag("oke-gate") +@Tag("oke-sequential") @IntegrationTest class ItMultiDomainModelsScale { From d6a5c1179c5c2d19613bbd831be260e9c5e04275 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 13 Sep 2024 12:24:31 -0400 Subject: [PATCH 166/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8cd48438fcd..1877a0db285 100644 --- a/pom.xml +++ b/pom.xml @@ -704,7 +704,7 @@ 3.0.1u2 2.0.20 4.12.0 - 3.9.0 + 3.9.1 1.78.1 5.11.0 5.7.1 From 20ce04e38fe9d86b2cd0120470bfd92515411a72 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Mon, 16 Sep 2024 16:47:45 +0000 Subject: [PATCH 167/356] Make ItCrossDomainTransactionSecurity OKE compatible --- .../ItCrossDomainTransactionSecurity.java | 122 ++++++++++++++++-- 1 file changed, 114 insertions(+), 8 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java index 9764a35982b..c66db521ec6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java @@ -12,10 +12,15 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1HTTPIngressPath; +import io.kubernetes.client.openapi.models.V1HTTPIngressRuleValue; +import io.kubernetes.client.openapi.models.V1IngressBackend; +import io.kubernetes.client.openapi.models.V1IngressRule; +import io.kubernetes.client.openapi.models.V1IngressServiceBackend; import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1ServiceBackendPort; import oracle.weblogic.domain.AuxiliaryImage; import oracle.weblogic.domain.Channel; import oracle.weblogic.domain.ClusterList; @@ -23,6 +28,8 @@ import oracle.weblogic.domain.DomainResource; import oracle.weblogic.kubernetes.actions.impl.AppParams; import oracle.weblogic.kubernetes.actions.impl.Cluster; +import oracle.weblogic.kubernetes.actions.impl.NginxParams; +import oracle.weblogic.kubernetes.actions.impl.Service; import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; @@ -42,6 +49,8 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOSTNAME; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -51,6 +60,8 @@ import static oracle.weblogic.kubernetes.actions.TestActions.buildAppArchive; import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; +import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; +import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReady; import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; @@ -60,18 +71,23 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runClientInsidePod; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runJavacInsidePod; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createIngressAndRetryIfFail; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretsForImageRepos; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -83,6 +99,7 @@ @DisplayName("Verify cross domain transaction is successful with CrossDomainSecurityEnabled set to true") @IntegrationTest @Tag("kind-parallel") +@Tag("oke-gate") class ItCrossDomainTransactionSecurity { private static final String auxImageName1 = DOMAIN_IMAGES_PREFIX + "domain1-cdxaction-aux"; @@ -106,7 +123,9 @@ class ItCrossDomainTransactionSecurity { private static String domain1AdminExtSvcRouteHost = null; private static String hostAndPort1 = null; private static String hostHeader1; - private static Map headers = null; + private static String nginxNamespace = null; + private static NginxParams nginxHelmParams = null; + private static int nginxNodePort; @@ -128,12 +147,24 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(1), "Namespace list is null"); domainNamespace = namespaces.get(1); + // get a unique Nginx namespace + logger.info("Assign a unique namespace for Nginx"); + assertNotNull(namespaces.get(2), "Namespace list is null"); + nginxNamespace = namespaces.get(2); + // Create the repo secret to pull the image // this secret is used only for non-kind cluster createTestRepoSecret(domainNamespace); // install and verify operator installAndVerifyOperator(opNamespace, domainNamespace); + + if (OKE_CLUSTER) { + logger.info("Installing Nginx controller using helm"); + // install and verify Nginx + nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); + } + buildDomains(); } @@ -165,13 +196,19 @@ void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostExceptio assertNotEquals(-1, domain1AdminServiceNodePort, "domain2 admin server default node port is not valid"); logger.info("domain2AdminServiceNodePort is: " + domain2AdminServiceNodePort); - hostAndPort1 = getHostAndPort(domain1AdminExtSvcRouteHost, domain1AdminServiceNodePort); - if (TestConstants.KIND_CLUSTER + if (OKE_CLUSTER) { + createNginxIngressPathRoutingRules(); + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + hostAndPort1 = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace); + } else { + hostAndPort1 = getHostAndPort(domain1AdminExtSvcRouteHost, domain1AdminServiceNodePort); + if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostHeader1 = createIngressHostRouting(domainNamespace, domainUid1, adminServerName, 7001); - hostAndPort1 = formatIPv6Host(getLocalHost().getHostAddress()) + hostHeader1 = createIngressHostRouting(domainNamespace, domainUid1, adminServerName, 7001); + hostAndPort1 = formatIPv6Host(getLocalHost().getHostAddress()) + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT; + } } logger.info("hostHeader1 for domain1 is: " + hostHeader1); logger.info("hostAndPort1 for domain1 is: " + hostAndPort1); @@ -216,7 +253,7 @@ void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostExceptio testUntil( runClientInsidePod(domain1AdminServerPodName, domainNamespace, "/u01", "JmsSendReceiveClient", - "t3://" + K8S_NODEPORT_HOST + ":" + t3ChannelPort1, "receive", "jms.admin.adminQueue", "1"), + "t3://" + "localhost" + ":" + "7001", "receive", "jms.admin.adminQueue", "1"), logger, "Wait for JMS Client to send/recv msg"); @@ -252,7 +289,7 @@ void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostExceptio testUntil( runClientInsidePod(domain1AdminServerPodName, domainNamespace, "/u01", "JmsSendReceiveClient", - "t3://" + K8S_NODEPORT_HOST + ":" + t3ChannelPort1, "receive", "jms.admin.adminQueue", "0"), + "t3://" + "localhost" + ":" + "7001", "receive", "jms.admin.adminQueue", "0"), logger, "Wait for JMS Client to send/recv msg"); } @@ -524,5 +561,74 @@ private String getCurlResult(String curlCmd) { return result.stdout(); } + private static void createNginxIngressPathRoutingRules() { + // create an ingress in domain namespace + final int ADMIN_SERVER_PORT = 7001; + String ingressName = domainNamespace + "-nginx-path-routing"; + String ingressClassName = nginxHelmParams.getIngressClassName(); + + // create ingress rules for two domains + List ingressRules = new ArrayList<>(); + List httpIngressPaths = new ArrayList<>(); + + V1HTTPIngressPath httpIngressPath = new V1HTTPIngressPath() + .path("/") + .pathType("Prefix") + .backend(new V1IngressBackend() + .service(new V1IngressServiceBackend() + .name(domainUid1 + "-admin-server") + .port(new V1ServiceBackendPort() + .number(ADMIN_SERVER_PORT))) + ); + httpIngressPaths.add(httpIngressPath); + + V1IngressRule ingressRule = new V1IngressRule() + .host("") + .http(new V1HTTPIngressRuleValue() + .paths(httpIngressPaths)); + + ingressRules.add(ingressRule); + + createIngressAndRetryIfFail(60, false, ingressName, domainNamespace, null, ingressClassName, ingressRules, null); + + // check the ingress was found in the domain namespace + assertThat(assertDoesNotThrow(() -> listIngresses(domainNamespace))) + .as(String.format("Test ingress %s was found in namespace %s", ingressName, domainNamespace)) + .withFailMessage(String.format("Ingress %s was not found in namespace %s", ingressName, domainNamespace)) + .contains(ingressName); + + logger.info("ingress {0} was created in namespace {1}", ingressName, domainNamespace); + + // check the ingress is ready to route the app to the server pod + String nginxServiceName = nginxHelmParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + nginxNodePort = assertDoesNotThrow(() -> Service.getServiceNodePort(nginxNamespace, nginxServiceName, "http"), + "Getting Nginx loadbalancer service node port failed"); + + String hostAndPort = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null + ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST + ":" + nginxNodePort; + + String curlCmd = "curl -g --silent --show-error --noproxy '*' http://" + hostAndPort + + "/weblogic/ready --write-out %{http_code} -o /dev/null"; + if (OKE_CLUSTER) { + try { + if (!callWebAppAndWaitTillReady(curlCmd, 60)) { + ExecResult result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); + logger.info(result.stdout()); + //restart core-dns service + result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); + logger.info(result.stdout()); + checkPodReady("core-dns", null, "kube-system"); + result = ExecCommand.exec(curlCmd); + logger.info(result.stdout()); + } + } catch (Exception ex) { + logger.warning(ex.getLocalizedMessage()); + } + } + + logger.info("Executing curl command {0}", curlCmd); + assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); + } + } From 38b97dbb263332a8e37c62749f7d3b86c83367ad Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 18 Sep 2024 15:50:21 +0000 Subject: [PATCH 168/356] changed starting execution time for nightlies in release/4.2 --- Jenkinsfile.oke | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index ac562bbc6f3..581c48bab64 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -5,9 +5,9 @@ CRON_SETTINGS_MAIN = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' -CRON_SETTINGS_42 = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false - H 1 * * * % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false - H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' +CRON_SETTINGS_42 = '''H 1 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false + H 11 * * * % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false + H 14 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-nightly' ? CRON_SETTINGS_MAIN : CRON_SETTINGS_42}" @@ -671,7 +671,7 @@ EOF ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform ${AVAILABILITY_DOMAIN} fi - if [ "${MAVEN_PROFILE_NAME}" = "oke-gate" ] && [ "${BRANCH}" = "main" ]; then + if [ "${MAVEN_PROFILE_NAME}" = "oke-gate" ] && [ "${BRANCH}" = "release/4.2" ]; then compname="wkt" wkt_compartment_ocid=$(oci iam compartment list --compartment-id-in-subtree true --all | jq --arg compname "$compname" '.data[] | select(."name"==$compname)' | jq -r ."id") sec_list_id=$(oci network security-list list --compartment-id="$wkt_compartment_ocid" --display-name=Security-List-wktiso1 | jq -r '.data[] | ."id"') From 422ede3d2f4af393849baeb4e457959610f8fe97 Mon Sep 17 00:00:00 2001 From: vanajakshi_mukkara Date: Mon, 23 Sep 2024 14:58:53 +0000 Subject: [PATCH 169/356] Adding upgrade tests covering ServerStartMode prod and secure --- Jenkinsfile.oke | 11 +- Jenkinsfile.podman | 4 +- .../ItLivenessProbeCustomization.java | 1 - .../ItMiiDomainUpgradeToSecureMode.java | 480 +++++++++++++++++- .../upgrade-startmode-prod.yaml | 30 ++ .../upgrade-startmode-secure.yaml | 32 ++ 6 files changed, 528 insertions(+), 30 deletions(-) create mode 100644 integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml create mode 100644 integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-secure.yaml diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 581c48bab64..2b1ae36597a 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -2,12 +2,11 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // -CRON_SETTINGS_MAIN = '''H 3 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false - H 2 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' - -CRON_SETTINGS_42 = '''H 1 * * * % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false - H 11 * * * % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false - H 14 * * * % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' +CRON_SETTINGS_MAIN = '''H 3 * * 1-5 % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false + H 2 * * 1-5 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' +CRON_SETTINGS_42 = '''H 1 * * 1-5 % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false + H 11 * * 1-5 % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false + H 14 * * 1-5 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-nightly' ? CRON_SETTINGS_MAIN : CRON_SETTINGS_42}" diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 766dcd2dcdb..fcf3f71d74d 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -18,8 +18,8 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * * % MAVEN_PROFILE_NAME=kind-sequential''' +CRON_SETTINGS = '''H 1 * * 1-5 % MAVEN_PROFILE_NAME=kind-parallel + H 2 * * 1-5 % MAVEN_PROFILE_NAME=kind-sequential''' pipeline { agent { label 'large-ol9u4' } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java index cd56ecae726..ba3877ef289 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java @@ -156,7 +156,6 @@ public static void initAll(@Namespaces(2) List namespaces) { */ @Test @DisplayName("Test custom liveness probe is triggered") - @Tag("gate") @Tag("crio") void testCustomLivenessProbeTriggered() { DomainResource domain1 = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace), diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java index bc27193c795..8c896dbac65 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java @@ -86,8 +86,10 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; +import static oracle.weblogic.kubernetes.utils.LoggingUtil.checkPodLogContainsString; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithTLSCertKey; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; @@ -103,6 +105,7 @@ * Verify all the servers in the domain comes up and WebLogic * console and REST management interfaces are accessible thru appropriate channels. * Verify deployed customer applications are accessible in appropriate channels and ports. + * Verify the mode remains the same before and after the upgrade. */ @DisplayName("Test upgrade to 1412 image for a mii domain") @IntegrationTest @@ -195,7 +198,8 @@ void afterEach() { for (int i = 1; i <= replicaCount; i++) { String managedServerPodName = managedServerPrefix + i; - testUntil(assertDoesNotThrow(() -> podDoesNotExist(managedServerPodName, domainUid, domainNamespace), + testUntil(withLongRetryPolicy, + assertDoesNotThrow(() -> podDoesNotExist(managedServerPodName, domainUid, domainNamespace), String.format("podDoesNotExist failed with ApiException for pod %s in namespace %s", managedServerPodName, domainNamespace)), logger, @@ -208,8 +212,9 @@ void afterEach() { } /** - * Test upgrade from 1411 container image to 1412 container image with production off and secure mode off. - * + * Test upgrade from 1411 container image to 1412 container image with ProductionModeEnabled as false and + * SecureModeEnabled false. + * Verify the mode is development before and after the upgrade. * Verify the sample application and console are available in default port 7001 before upgrade. * Verify the management REST interface continue to be available in default port 7001 before and after upgrade. * Verify the sample application continue to available in default port 7001 after upgrade. @@ -249,8 +254,11 @@ void testUpgrade1411to1412ProdOff() throws UnknownHostException { //create ingress resources to route traffic to various service endpoints createNginxIngressHostRouting(domainUid, 7001, 7002, 8001, nginxParams.getIngressClassName(), false); DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); - logger.info(Yaml.dump(dcr)); - + logger.info(Yaml.dump(dcr)); + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in development mode"); + //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); @@ -274,7 +282,10 @@ void testUpgrade1411to1412ProdOff() throws UnknownHostException { //upgrade domain to use 1412 images upgradeImage(domainNamespace, domainUid, image1412); - + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in development mode"); + //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); //verify sample app is available in admin server in port 7001 @@ -292,8 +303,9 @@ void testUpgrade1411to1412ProdOff() throws UnknownHostException { } /** - * Test upgrade from 1411 container image to 1412 container image with production on and secure mode off. - * + * Test upgrade from 1411 container image to 1412 container image with ProductionModeEnabled as true and + * SecureModeEnabled as false. + * Verify the server is running in production mode and secure mode is disabled both before and update the upgrade. * Verify the sample application and console are available in default port 7001 before upgrade. * Verify the management REST interface continue to be available in default port 7001 before and after upgrade. * Verify the sample application continue to available in default port 7001 after upgrade. @@ -331,6 +343,9 @@ void testUpgrade1411to1412ProdOnSecOff() throws UnknownHostException { createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, null); DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); logger.info(Yaml.dump(dcr)); + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); //create ingress resources to route traffic to various service endpoints createNginxIngressHostRouting(domainUid, 7001, 7002, 8001, nginxParams.getIngressClassName(), false); @@ -359,6 +374,10 @@ void testUpgrade1411to1412ProdOnSecOff() throws UnknownHostException { upgradeImage(domainNamespace, domainUid, image1412); //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + //verify sample app is available in admin server in port 7001 verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, sampleAppUri, adminServerName, true, ingressIP); @@ -374,8 +393,9 @@ void testUpgrade1411to1412ProdOnSecOff() throws UnknownHostException { } /** - * Test upgrade from 1411 container image to 1412 container image with production and secure mode on. - * + * Test upgrade from 1411 container image to 1412 container image with ProductionModeEnabled as true and + * SecureModeEnabled as true. + * Verify the server is running in production mode and secure mode is enabled both before and after the upgrade. * Verify all services are available only in HTTPS in adminserver as well as managed servers. * Verify the admin server sample application is available in default port 7002 before upgrade and after upgrade. * Verify the management REST interface continue to be available in default admin port 9002 before and after upgrade. @@ -414,6 +434,13 @@ void testUpgrade1411to1412ProdOnSecOn() throws UnknownHostException { createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, channelName); DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); logger.info(Yaml.dump(dcr)); + + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "Secure mode enabled"); + //create ingress resources to route traffic to various service endpoints createNginxIngressHostRouting(domainUid, 9002, 7002, 8500, nginxParams.getIngressClassName(), true); @@ -442,6 +469,13 @@ void testUpgrade1411to1412ProdOnSecOn() throws UnknownHostException { upgradeImage(domainNamespace, domainUid, image1412); dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); logger.info(Yaml.dump(dcr)); + + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "Secure mode enabled"); + //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); @@ -460,8 +494,9 @@ void testUpgrade1411to1412ProdOnSecOn() throws UnknownHostException { } /** - * Test upgrade from 1411 container image to 1412 container image with production on and secure mode not configured. - * + * Test upgrade from 1411 container image to 1412 container image with ProductionModeEnabled as true + * and secure mode not configured. + * Verify the server is running in production mode both before and update the upgrade. * Verify the sample application available at default port 7001 before and after upgrade. * Verify the console and REST management interfaces available at default * administration port 9002 before upgrade and only REST management interfaces available at @@ -499,6 +534,10 @@ void testUpgrade1411to1412ProdOnSecNotConfigured() throws UnknownHostException { createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, channelName); DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); logger.info(Yaml.dump(dcr)); + // check server logs for production mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + //create ingress resource to route administration traffic to admin server secure service endpoint String administrationIngressHost = createAdministrationIngressHostRouting(domainUid, 9002, nginxParams.getIngressClassName(), true); @@ -531,6 +570,10 @@ void testUpgrade1411to1412ProdOnSecNotConfigured() throws UnknownHostException { dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); logger.info(Yaml.dump(dcr)); + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); @@ -549,8 +592,9 @@ void testUpgrade1411to1412ProdOnSecNotConfigured() throws UnknownHostException { } /** - * Test upgrade from 12214 to 1412 with production off and secure mode off. - * + * Test upgrade from 12214 to 1412 with ProductionModeEnabled as false. + * + * Verify the server is running in development mode both before and update the upgrade. * Verify the sample application and console are available in default port 7001 before upgrade. * Verify the management REST interface continue to be available in default port 7001 before and after upgrade. * Verify the sample application continue to available in default port 7001 after upgrade. @@ -589,8 +633,12 @@ void testUpgrade12214to1412ProdOff() throws UnknownHostException { //create ingress resources to route traffic to various service endpoints createNginxIngressHostRouting(domainUid, 7001, 7002, 8001, nginxParams.getIngressClassName(), false); DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); - logger.info(Yaml.dump(dcr)); - + logger.info(Yaml.dump(dcr)); + + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in development mode"); + //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); @@ -617,6 +665,10 @@ void testUpgrade12214to1412ProdOff() throws UnknownHostException { //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + // check server logs for development mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in development mode"); //verify sample app is available in admin server in port 7001 verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, @@ -633,9 +685,9 @@ void testUpgrade12214to1412ProdOff() throws UnknownHostException { } /** - * Test upgrade from 12214 container image to - * 1412 container image with production on and secure mode not configured. - * + * Test upgrade from 12214 container image to 1412 container image with ProductionModeEnabled as true and + * AdminPortEnabled as true. + * Verify the servers are running in production mode and admin port is enabled both before and after the upgrade. * Verify the sample application available at default port 7001 before and after upgrade. * Verify the console and REST management interfaces available at default * administration port 9002 before upgrade and only REST management interfaces available at @@ -645,7 +697,7 @@ void testUpgrade12214to1412ProdOff() throws UnknownHostException { */ @Test @DisplayName("Test upgrade from 12214 to 1412 with production on and administration port enabled") - void testUpgrade12214to1412ProdOn() throws UnknownHostException { + void testUpgrade12214to1412ProdOnAndAdminPortOn() throws UnknownHostException { domainNamespace = namespaces.get(7); domainUid = "testdomain6"; adminServerPodName = domainUid + "-" + adminServerName; @@ -673,6 +725,12 @@ void testUpgrade12214to1412ProdOn() throws UnknownHostException { createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, channelName); DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); logger.info(Yaml.dump(dcr)); + // check server logs for production mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "ADMIN_PORT_SECURE='true'"); + //create ingress resource to route administration traffic to admin server secure service endpoint String administrationIngressHost = createAdministrationIngressHostRouting(domainUid, 9002, nginxParams.getIngressClassName(), true); @@ -707,7 +765,13 @@ void testUpgrade12214to1412ProdOn() throws UnknownHostException { logger.info(Yaml.dump(dcr)); //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); - + + // check server logs for production mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "ADMIN_PORT_SECURE='true'"); + //verify REST access is available in admin server port 9002 verifyAppServerAccess(true, getNginxLbNodePort("https"), true, administrationIngressHost, applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); @@ -722,6 +786,380 @@ void testUpgrade12214to1412ProdOn() throws UnknownHostException { adminAppUri, adminAppText, true, ingressIP); } + /** + * Test upgrade from 12214 container image to 1412 container image with ProductionModeEnabled as true and + * SecureModeEnabled as false. + * + * Verify the servers are running in production mode and secure mode is disabled both before and after the upgrade. + * Verify the sample application and console are available in default port 7001 before upgrade. + * Verify the management REST interface continue to be available in default port 7001 before and after upgrade. + * Verify the sample application continue to available in default port 7001 after upgrade. + * Verify the console is moved to a new location in 1412. + * + */ + @Test + @DisplayName("Test upgrade from 12214 to 1412 with production on and secure mode off") + void testUpgrade12214to1412ProdOnSecOff() throws UnknownHostException { + domainNamespace = namespaces.get(3); + domainUid = "testdomain7"; + adminServerPodName = domainUid + "-" + adminServerName; + // create WDT properties file for the WDT model + Path wdtVariableFile = Paths.get(WORK_DIR, this.getClass().getSimpleName(), "wdtVariable.properties"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile); + Files.createDirectories(wdtVariableFile.getParent()); + Files.writeString(wdtVariableFile, "SSLEnabled=false\n", StandardOpenOption.CREATE); + Files.writeString(wdtVariableFile, "DomainName=" + domainUid + "\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile, "ProductionModeEnabled=true\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile, "SecureModeEnabled=false\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile, "AdministrationPortEnabled=false\n", StandardOpenOption.APPEND); + }); + + String auxImageName = DOMAIN_IMAGES_PREFIX + "dci-prodon"; + String auxImageTag = getDateAndTimeStamp(); + Path wdtModelFile = Paths.get(RESOURCE_DIR, "securemodeupgrade", "upgrade-model.yaml"); + + // create auxiliary domain creation image + String auxImage = createAuxImage(auxImageName, auxImageTag, wdtModelFile.toString(), wdtVariableFile.toString()); + String baseImage = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag12214; + //name of channel available in domain configuration + String channelName = "default"; + // create auxiliary domain creation image + createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, null); + DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + logger.info(Yaml.dump(dcr)); + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + + + //create ingress resources to route traffic to various service endpoints + createNginxIngressHostRouting(domainUid, 7001, 7002, 8001, nginxParams.getIngressClassName(), false); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + String ingressServiceName = nginxParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + //get ingress ip of the ingress controller to send http requests to servers in domain + ingressIP = getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) != null + ? getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) : K8S_NODEPORT_HOST; + + //verify sample app is available in admin server in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify admin console is available in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample application is available in cluster address + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + + //upgrade domain to use 1412 images + upgradeImage(domainNamespace, domainUid, image1412); + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + //verify sample app is available in admin server in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify sample app is available in admin server in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample application is available in cluster address + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + } + + /** + * Test upgrade from 12214 container image to 1412 container image with ProductionModeEnabled as true and + * SecureModeEnabled as true. + * Verify the servers are started in production mode and secure mode is enabled both before and after the upgrade. + * Verify all services are available only in HTTPS in adminserver as well as managed servers. + * Verify the admin server sample application is available in default port 7002 before upgrade and after upgrade. + * Verify the management REST interface continue to be available in default admin port 9002 before and after upgrade. + * Verify the cluster sample application continue to available in default port 8500 before and after upgrade. + * Verify the console is moved to a new location in 1412. + * + */ + @Test + @DisplayName("Test upgrade from 12214 to 1412 with production on and secure mode on") + void testUpgrade12214to1412ProdOnSecOn() throws UnknownHostException { + domainNamespace = namespaces.get(4); + domainUid = "testdomain8"; + adminServerPodName = domainUid + "-" + adminServerName; + // create WDT properties file for the WDT model + Path wdtVariableFile = Paths.get(WORK_DIR, this.getClass().getSimpleName(), "wdtVariable.properties"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile); + Files.createDirectories(wdtVariableFile.getParent()); + Files.writeString(wdtVariableFile, "SSLEnabled=true\n", StandardOpenOption.CREATE); + Files.writeString(wdtVariableFile, "DomainName=" + domainUid + "\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile, "ProductionModeEnabled=true\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile, "SecureModeEnabled=true\n", StandardOpenOption.APPEND); + Files.writeString(wdtVariableFile, "AdministrationPortEnabled=true\n", StandardOpenOption.APPEND); + }); + + String auxImageName = DOMAIN_IMAGES_PREFIX + "dci-securemodeon"; + String auxImageTag = getDateAndTimeStamp(); + Path wdtModelFile = Paths.get(RESOURCE_DIR, "securemodeupgrade", "upgrade-model.yaml"); + + // create auxiliary domain creation image + String auxImage = createAuxImage(auxImageName, auxImageTag, wdtModelFile.toString(), wdtVariableFile.toString()); + String baseImage = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag12214; + //name of channel available in domain configuration + String channelName = "internal-admin"; + //create a MII domain resource with the auxiliary image + createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, channelName); + DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + logger.info(Yaml.dump(dcr)); + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "Secure mode enabled"); + + //create ingress resources to route traffic to various service endpoints + createNginxIngressHostRouting(domainUid, 9002, 7002, 8500, nginxParams.getIngressClassName(), true); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + String ingressServiceName = nginxParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + //get ingress ip of the ingress controller to send http requests to servers in domain + ingressIP = getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) != null + ? getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) : K8S_NODEPORT_HOST; + + //verify admin console is available in port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample app is available in admin server in secure port 7002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminAppIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify sample application is available in cluster address secure port 8500 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + + //upgrade domain to use 1412 images + upgradeImage(domainNamespace, domainUid, image1412); + dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + logger.info(Yaml.dump(dcr)); + + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "Secure mode enabled"); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + //verify admin console is available in port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample app is available in admin server in secure port 7002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminAppIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify sample application is available in cluster address secure port 8500 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + } + + /** + * Test upgrade from 12214 container image to 1412 container image with ServerStartMode as prod. + * Verify the servers are started in production mode both before and after the upgrade. + * Verify the sample application and console are available in default port 7001 before upgrade. + * Verify the management REST interface continue to be available in default port 7001 before and after upgrade. + * Verify the sample application continue to available in default port 7001 after upgrade. + * Verify the console is moved to a new location in 1412. + * + */ + @Test + @DisplayName("Test upgrade from 12214 to 1412 with serverStartMode prod") + void testUpgrade12214to1412ServerStartModeProd() throws UnknownHostException { + domainNamespace = namespaces.get(3); + domainUid = "testdomain9"; + adminServerPodName = domainUid + "-" + adminServerName; + // create WDT properties file for the WDT model + Path wdtVariableFile = Paths.get(WORK_DIR, this.getClass().getSimpleName(), "wdtVariable.properties"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile); + Files.createDirectories(wdtVariableFile.getParent()); + Files.writeString(wdtVariableFile, "DomainName=" + domainUid + "\n", StandardOpenOption.CREATE); + }); + + String auxImageName = DOMAIN_IMAGES_PREFIX + "dci-prodon"; + String auxImageTag = getDateAndTimeStamp(); + Path wdtModelFile = Paths.get(RESOURCE_DIR, "securemodeupgrade", "upgrade-startmode-prod.yaml"); + + // create auxiliary domain creation image + String auxImage = createAuxImage(auxImageName, auxImageTag, wdtModelFile.toString(), wdtVariableFile.toString()); + String baseImage = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag12214; + //name of channel available in domain configuration + String channelName = "default"; + // create auxiliary domain creation image + createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, null); + DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + logger.info(Yaml.dump(dcr)); + // check server logs for production mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + + //create ingress resources to route traffic to various service endpoints + createNginxIngressHostRouting(domainUid, 7001, 7002, 7100, nginxParams.getIngressClassName(), false); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + String ingressServiceName = nginxParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + //get ingress ip of the ingress controller to send http requests to servers in domain + ingressIP = getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) != null + ? getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) : K8S_NODEPORT_HOST; + + //verify sample app is available in admin server in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify admin console is available in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample application is available in cluster address + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + + //upgrade domain to use 1412 images + upgradeImage(domainNamespace, domainUid, image1412); + // check server logs for production mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + //verify sample app is available in admin server in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify sample app is available in admin server in port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 7001 + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample application is available in cluster address + verifyAppServerAccess(false, getNginxLbNodePort("http"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + } + + /** + * Test upgrade from 12214 container image to 1412 container image with ServerStartMode as secure. + * Verify the servers are started in production mode and secure mode is enabled both before and after the upgrade. + * Verify all services are available only in HTTPS in adminserver as well as managed servers. + * Verify the admin server sample application is available in default ssl port 7002 before upgrade and after upgrade. + * Verify the management REST interface continue to be available in default admin port 9002 before and after upgrade. + * Verify the cluster sample application continue to available in ssl port 8500 before and after upgrade. + * Verify the console is moved to a new location in 1412. + * + */ + @Test + @DisplayName("Test upgrade from 12214 to 1412 with server start mode secure") + void testUpgrade12214to1412ServerStartModeSecure() throws UnknownHostException { + domainNamespace = namespaces.get(4); + domainUid = "testdomain10"; + adminServerPodName = domainUid + "-" + adminServerName; + // create WDT properties file for the WDT model + Path wdtVariableFile = Paths.get(WORK_DIR, this.getClass().getSimpleName(), "wdtVariable.properties"); + assertDoesNotThrow(() -> { + Files.deleteIfExists(wdtVariableFile); + Files.createDirectories(wdtVariableFile.getParent()); + Files.writeString(wdtVariableFile, "DomainName=" + domainUid + "\n", StandardOpenOption.CREATE); + Files.writeString(wdtVariableFile, "SSLEnabled=true\n", StandardOpenOption.APPEND); + }); + + String auxImageName = DOMAIN_IMAGES_PREFIX + "dci-securemodeon"; + String auxImageTag = getDateAndTimeStamp(); + Path wdtModelFile = Paths.get(RESOURCE_DIR, "securemodeupgrade", "upgrade-startmode-secure.yaml"); + + // create auxiliary domain creation image + String auxImage = createAuxImage(auxImageName, auxImageTag, wdtModelFile.toString(), wdtVariableFile.toString()); + String baseImage = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag12214; + //name of channel available in domain configuration + String channelName = "internal-admin"; + //create a MII domain resource with the auxiliary image + createDomainUsingAuxiliaryImage(domainNamespace, domainUid, baseImage, auxImage, channelName); + DomainResource dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + logger.info(Yaml.dump(dcr)); + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "Secure mode enabled"); + + //create ingress resources to route traffic to various service endpoints + createNginxIngressHostRouting(domainUid, 9002, 7002, 8500, nginxParams.getIngressClassName(), true); + + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + String ingressServiceName = nginxParams.getHelmParams().getReleaseName() + "-ingress-nginx-controller"; + //get ingress ip of the ingress controller to send http requests to servers in domain + ingressIP = getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) != null + ? getServiceExtIPAddrtOke(ingressServiceName, ingressNamespace) : K8S_NODEPORT_HOST; + + //verify admin console is available in port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample app is available in admin server in secure port 7002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminAppIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify sample application is available in cluster address secure port 8500 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + + //upgrade domain to use 1412 images + upgradeImage(domainNamespace, domainUid, image1412); + // check server logs for production mode and secure mode + checkPodLogContainsString(domainNamespace, adminServerPodName, + "running in production mode"); + checkPodLogContainsString(domainNamespace, adminServerPodName, + "Secure mode enabled"); + dcr = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace)); + logger.info(Yaml.dump(dcr)); + //verify the number of channels available in the domain resource match with the count and name + verifyChannel(domainNamespace, domainUid, List.of(channelName)); + + //verify admin console is available in port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + adminAppUri, adminAppText, true, ingressIP); + //verify REST access is available in admin server port 9002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminIngressHost, + applicationRuntimes, MII_BASIC_APP_NAME, true, ingressIP); + //verify sample app is available in admin server in secure port 7002 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, adminAppIngressHost, + sampleAppUri, adminServerName, true, ingressIP); + //verify sample application is available in cluster address secure port 8500 + verifyAppServerAccess(true, getNginxLbNodePort("https"), true, clusterIngressHost, + sampleAppUri, msName, true, ingressIP); + } + + /** * Create domain custom resource with auxiliary image, base image and channel name. * diff --git a/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml b/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml new file mode 100644 index 00000000000..4ca2c37a9a4 --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'prod' +topology: + Name: '@@PROP:DomainName@@' + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + ListenPort: 7100 + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file diff --git a/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-secure.yaml b/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-secure.yaml new file mode 100644 index 00000000000..b50c80b4baa --- /dev/null +++ b/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-secure.yaml @@ -0,0 +1,32 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@SECRET:__weblogic-credentials__:username@@' + AdminPassword: '@@SECRET:__weblogic-credentials__:password@@' + ServerStartMode: 'secure' +topology: + Name: '@@PROP:DomainName@@' + AdminServerName: adminserver + ServerTemplate: + myserver-template: + Cluster: mycluster + SSL: + Enabled: '@@PROP:SSLEnabled@@' + ListenPort: 8500 + Cluster: + mycluster: + DynamicServers: + ServerTemplate: myserver-template + ServerNamePrefix: mycluster-ms- + DynamicClusterSize: 2 + MinDynamicClusterSize: 0 + CalculatedListenPorts: false +appDeployments: + Application: + sample-app: + Target: [ + mycluster, + adminserver + ] + SourcePath: wlsdeploy/applications/sample-app.ear \ No newline at end of file From 74eaa3596619558acafc9e829ef80348df1b4dd8 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Tue, 24 Sep 2024 13:19:35 +0000 Subject: [PATCH 170/356] Added checks for created in OCI load balancers health state and delete it in the end of tests to avoid left overs after OKE cluster deletion --- Jenkinsfile.oke | 7 + integration-tests/pom.xml | 1 + .../kubernetes/ItCrossDomainTransaction.java | 4 +- .../kubernetes/ItOCILoadBalancer.java | 30 +--- .../weblogic/kubernetes/TestConstants.java | 1 + .../extensions/InitializationTasks.java | 19 ++ .../kubernetes/utils/LoadBalancerUtils.java | 162 +++++++++++++++++- .../bash-scripts/delete_loadbalancer.sh | 25 +++ 8 files changed, 217 insertions(+), 32 deletions(-) create mode 100755 integration-tests/src/test/resources/bash-scripts/delete_loadbalancer.sh diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 2b1ae36597a..2d9ecdb73ac 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -2,12 +2,15 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // + CRON_SETTINGS_MAIN = '''H 3 * * 1-5 % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false H 2 * * 1-5 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' CRON_SETTINGS_42 = '''H 1 * * 1-5 % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false H 11 * * 1-5 % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false H 14 * * 1-5 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' + + CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-nightly' ? CRON_SETTINGS_MAIN : CRON_SETTINGS_42}" pipeline { @@ -527,6 +530,8 @@ EOF export OCI_CLI_CONFIG_FILE=${jenkins_home_directory}/.oci/config export OCI_CLI_PROFILE=${oci_profile} compartment_ocid=${compartment_id} + export COMPARTMENT_OCID=${compartment_id} + echo "COMPARTMENT_OCID : ${COMPARTMENT_OCID}" mkdir -m777 -p "${WORKSPACE}/.mvn" touch ${WORKSPACE}/.mvn/maven.config export KUBECONFIG=${kubeconfig_file} @@ -597,6 +602,7 @@ EOF echo "-Dwko.it.fmwinfra.image.tag=\"${FMWINFRA_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.db.image.name=\"${DB_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.db.image.tag=\"${DB_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.oci.compartment.ocid=\"${COMPARTMENT_OCID}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.monitoring.exporter.branch=\"${MONITORING_EXPORTER_BRANCH}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.monitoring.exporter.webapp.version=\"${MONITORING_EXPORTER_WEBAPP_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config @@ -618,6 +624,7 @@ EOF export PATH=${runtime_path} export OKE_CLUSTER="true" export OKE_CLUSTER_PRIVATEIP="true" + export COMPARTMENT_OCID=${compartment_id} export OKD="false" export KUBECONFIG=${kubeconfig_file} export BASE_IMAGES_REPO_USERNAME="${OCIR_USER}" diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 45f6ca2befd..e05238bebed 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -48,6 +48,7 @@ ${env.GRAFANA_CHART_VERSION} ${env.ISTIO_VERSION} ${env.OKE_CLUSTER} + ${env.COMPARTMENT_OCID} ${env.ARM} ${env.OKE_CLUSTER_PRIVATEIP} ${env.NFS_SERVER} diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index 2788d99b9b0..94f460c2ea7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -693,7 +693,7 @@ private static void createNginxIngressPathRoutingRules() { + "/weblogic/ready --write-out %{http_code} -o /dev/null"; if (OKE_CLUSTER) { try { - if (!callWebAppAndWaitTillReady(curlCmd, 60)) { + if (!callWebAppAndWaitTillReady(curlCmd, 20)) { ExecResult result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); logger.info(result.stdout()); //restart core-dns service @@ -709,6 +709,6 @@ private static void createNginxIngressPathRoutingRules() { } logger.info("Executing curl command {0}", curlCmd); - assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); + assertTrue(callWebAppAndWaitTillReady(curlCmd, 20)); } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java index 799158e10fd..da2d655f7ce 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java @@ -4,12 +4,8 @@ package oracle.weblogic.kubernetes; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import io.kubernetes.client.openapi.models.V1LoadBalancerIngress; -import io.kubernetes.client.openapi.models.V1Service; import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; @@ -25,10 +21,10 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; -import static oracle.weblogic.kubernetes.assertions.impl.Kubernetes.getService; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndCheckForServerNameInResponse; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createMiiDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.getLoadBalancerIP; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyOCILoadBalancer; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -124,30 +120,6 @@ void testOCILoadBalancer() throws Exception { verifyWebAppAccessThroughOCILoadBalancer(loadBalancerIP, 2, clusterHttpPort); } - /** - * Retreive external IP from OCI LoadBalancer. - */ - private static String getLoadBalancerIP(String namespace, String lbName) throws Exception { - Map labels = new HashMap<>(); - labels.put("loadbalancer", lbName); - V1Service service = getService(lbName, labels, namespace); - assertNotNull(service, "Can't find service with name " + lbName); - logger.info("Found service with name {0} in {1} namespace ", lbName, namespace); - assertNotNull(service.getStatus(), "service status is null"); - assertNotNull(service.getStatus().getLoadBalancer(), "service loadbalancer is null"); - List ingress = service.getStatus().getLoadBalancer().getIngress(); - if (ingress != null) { - logger.info("LoadBalancer Ingress " + ingress.toString()); - V1LoadBalancerIngress lbIng = - ingress.stream().filter(c -> c.getIp() != null && !c.getIp().equals("pending")).findAny().orElse(null); - if (lbIng != null) { - logger.info("OCI LoadBalancer is created with external ip" + lbIng.getIp()); - return lbIng.getIp(); - } - } - return null; - } - /** * Verify the sample-app app can be accessed from all managed servers in the domain through OCI Load Balancer. */ diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index d4268747962..3da2206696c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -370,6 +370,7 @@ public interface TestConstants { public static final String MYSQL_VERSION = "8.0.29"; //OKE constants + public static final String COMPARTMENT_OCID = System.getProperty("wko.it.oci.compartment.ocid", ""); public static final boolean OKE_CLUSTER = Boolean.parseBoolean(getNonEmptySystemProperty("wko.it.oke.cluster", "false")); public static final boolean OKE_CLUSTER_PRIVATEIP = diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 4ae6b74af59..2af540f0238 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -113,6 +113,7 @@ import static oracle.weblogic.kubernetes.utils.FileUtils.cleanupDirectory; import static oracle.weblogic.kubernetes.utils.IstioUtils.installIstio; import static oracle.weblogic.kubernetes.utils.IstioUtils.uninstallIstio; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.deleteLoadBalancer; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyTraefik; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -133,6 +134,7 @@ public class InitializationTasks implements BeforeAllCallback, ExtensionContext. private static String wdtBasicImage; private static Collection pushedImages = new ArrayList<>(); + private static Collection lbIPs = new ArrayList<>(); private static boolean isInitializationSuccessful = false; ConditionFactory withVeryLongRetryPolicy @@ -355,6 +357,15 @@ public static void registerPushedImage(String imageName) { pushedImages.add(imageName); } + /** + * Called when load balancer is created in OCI, allowing conditional cleanup of load balancers. + * + * @param lbIP external IP of load balancer. + */ + public static void registerLoadBalancerExternalIP(String lbIP) { + lbIPs.add(lbIP); + } + @Override public void close() { LoggingFacade logger = getLogger(); @@ -395,6 +406,14 @@ public void close() { for (String image : pushedImages) { deleteImage(image); } + + if (OKE_CLUSTER) { + logger.info("Cleanup created in OCI load balancers after all test suites are run"); + // delete all load balancers in OCI + for (String ip : lbIPs) { + deleteLoadBalancer(ip); + } + } } // delete images from TEST_IMAGES_REPO, if necessary diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index ee717d91360..5e5b67828fc 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -12,6 +12,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import io.kubernetes.client.custom.IntOrString; import io.kubernetes.client.openapi.ApiException; @@ -21,6 +22,7 @@ import io.kubernetes.client.openapi.models.V1IngressRule; import io.kubernetes.client.openapi.models.V1IngressServiceBackend; import io.kubernetes.client.openapi.models.V1IngressTLS; +import io.kubernetes.client.openapi.models.V1LoadBalancerIngress; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServiceBackendPort; @@ -29,10 +31,13 @@ import oracle.weblogic.kubernetes.TestConstants; import oracle.weblogic.kubernetes.actions.impl.NginxParams; import oracle.weblogic.kubernetes.actions.impl.TraefikParams; +import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.HelmParams; +import oracle.weblogic.kubernetes.extensions.InitializationTasks; import oracle.weblogic.kubernetes.logging.LoggingFacade; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.COMPARTMENT_OCID; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_NAME; @@ -40,6 +45,7 @@ import static oracle.weblogic.kubernetes.TestConstants.NGINX_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.NGINX_REPO_NAME; import static oracle.weblogic.kubernetes.TestConstants.NGINX_REPO_URL; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_CHART_NAME; @@ -53,10 +59,12 @@ import static oracle.weblogic.kubernetes.actions.TestActions.installTraefik; import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; import static oracle.weblogic.kubernetes.actions.TestActions.upgradeTraefikImage; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Command.defaultCommandParams; import static oracle.weblogic.kubernetes.assertions.TestAssertions.isHelmReleaseDeployed; import static oracle.weblogic.kubernetes.assertions.TestAssertions.isNginxReady; import static oracle.weblogic.kubernetes.assertions.TestAssertions.isOCILoadBalancerReady; import static oracle.weblogic.kubernetes.assertions.TestAssertions.isTraefikReady; +import static oracle.weblogic.kubernetes.assertions.impl.Kubernetes.getService; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReady; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; @@ -126,6 +134,14 @@ public static void installAndVerifyOCILoadBalancer( logger, "external IP to be generated in {0}", namespace); + if (OKE_CLUSTER) { + testUntil( + assertDoesNotThrow(() -> isLoadBalancerHealthy(namespace, loadBalancerName), + "isLoadBalancerHealthy failed with ApiException"), + logger, + "OCI LB to be healthy in namespace {0}", + namespace); + } } /** @@ -212,6 +228,22 @@ public static NginxParams installAndVerifyNginx(String nginxNamespace, logger, "NGINX to be ready in namespace {0}", nginxNamespace); + if (OKE_CLUSTER) { + testUntil( + assertDoesNotThrow(() -> isOCILoadBalancerReady( + nginxHelmParams.getReleaseName() + "-ingress-nginx-controller", + null, nginxNamespace), "isOCILoadBalancerReady failed with ApiException"), + logger, + "external IP to be generated in {0}", + nginxNamespace); + testUntil( + assertDoesNotThrow(() -> isLoadBalancerHealthy(nginxNamespace, + nginxHelmParams.getReleaseName() + "-ingress-nginx-controller"), + "isLoadBalancerHealthy failed with ApiException"), + logger, + "NGINX LB to be healthy in namespace {0}", + nginxNamespace); + } return nginxParams; } @@ -282,10 +314,103 @@ public static TraefikParams installAndVerifyTraefik(String traefikNamespace, logger, "Traefik to be ready in namespace {0}", traefikNamespace); - + if (OKE_CLUSTER) { + testUntil( + assertDoesNotThrow(() -> isOCILoadBalancerReady( + traefikHelmParams.getReleaseName(), + null, traefikNamespace), "isOCILoadBalancerReady failed with ApiException"), + logger, + "external IP to be generated in {0}", + traefikNamespace); + testUntil( + assertDoesNotThrow(() -> isLoadBalancerHealthy(traefikNamespace, traefikHelmParams.getReleaseName()), + "isLoadBalancerHealthy failed with ApiException"), + logger, + "Traefik to be healthy in namespace {0}", + traefikNamespace); + } return traefikParams; } + /** + * Check lb has healty status. + * + * @param namespace in which to check for lb controller + * @name service name of lb controller + * @return true if healthy, false otherwise + */ + public static Callable isLoadBalancerHealthy(String namespace, String name) { + return () -> checkLoadBalancerHealthy(namespace, name); + } + + /** + * Retreive external IP from OCI LoadBalancer. + * + * @param namespace - namespace + * @param lbName -loadbalancer service name + */ + public static String getLoadBalancerIP(String namespace, String lbName) throws Exception { + Map labels = new HashMap<>(); + labels.put("loadbalancer", lbName); + V1Service service = getService(lbName, null, namespace); + assertNotNull(service, "Can't find service with name " + lbName); + LoggingFacade logger = getLogger(); + logger.info("Found service with name {0} in {1} namespace ", lbName, namespace); + assertNotNull(service.getStatus(), "service status is null"); + assertNotNull(service.getStatus().getLoadBalancer(), "service loadbalancer is null"); + List ingress = service.getStatus().getLoadBalancer().getIngress(); + if (ingress != null) { + logger.info("LoadBalancer Ingress " + ingress.toString()); + V1LoadBalancerIngress lbIng = + ingress.stream().filter(c -> c.getIp() != null && !c.getIp().equals("pending")).findAny().orElse(null); + if (lbIng != null) { + logger.info("OCI LoadBalancer is created with external ip" + lbIng.getIp()); + return lbIng.getIp(); + } + } + return null; + } + + private static boolean checkLoadBalancerHealthy(String namespace, String lbServiceName) { + String lbPublicIP = assertDoesNotThrow(() -> getLoadBalancerIP(namespace, lbServiceName)); + InitializationTasks.registerLoadBalancerExternalIP(lbPublicIP); + LoggingFacade logger = getLogger(); + String testcompartmentid = System.getProperty("wko.it.oci.compartment.ocid"); + logger.info("wko.it.oci.compartment.ocid property " + testcompartmentid); + + final String command = "oci lb load-balancer list --compartment-id " + + testcompartmentid + " --query \"data[?contains(\\\"ip-addresses\\\"[0].\\\"ip-address\\\", '" + + lbPublicIP + "')].id | [0]\" --raw-output --all"; + + logger.info("Command to retrieve Load Balancer OCID is: {0} ", command); + + ExecResult result = assertDoesNotThrow(() -> exec(command, true)); + logger.info("The command returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + + if (result == null || result.exitValue() != 0 || result.stdout() == null) { + return false; + } + + // Clean up the string to extract the Load Balancer ID + String lbOCID = result.stdout().trim(); + + //check health status + final String command1 = "oci lb load-balancer-health get --load-balancer-id " + lbOCID; + logger.info("Command to retrieve Load Balancer health status is: {0} ", command1); + result = assertDoesNotThrow(() -> exec(command1, true)); + logger.info("The command returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + + if (result == null || result.exitValue() != 0 || result.stdout() == null) { + return false; + } + + return result.stdout().contains("OK"); + + } + + /** Upgrade Traefik and wait for up to five minutes for the Traefik pod to be ready. * * @param traefikNamespace the namespace in which the Traefik ingress controller is installed @@ -732,4 +857,39 @@ public static String getLbExternalIp(String lbrelname, String lbns) throws Excep return result.stdout().trim(); } + + /** + * Delete Load Balancer , created in OCI by using provided public IP. + * + * @param lbPublicIP public Load Balancer IP + */ + public static void deleteLoadBalancer(String lbPublicIP) { + if (!lbPublicIP.isEmpty()) { + if (lbPublicIP.startsWith("[") && lbPublicIP.endsWith("]")) { + // Remove the brackets and return the content inside + lbPublicIP = lbPublicIP.substring(1, lbPublicIP.length() - 1); + } + LoggingFacade logger = getLogger(); + Path deleteLBPath = + Paths.get(RESOURCE_DIR, "bash-scripts", "delete_loadbalancer.sh"); + String deleteLBScript = deleteLBPath.toString(); + String command = + String.format("chmod 777 %s ", deleteLBScript); + logger.info("Delete Load Balancer command {0}", command); + assertTrue(() -> Command.withParams( + defaultCommandParams() + .command(command) + .redirect(true)) + .execute()); + + String command1 = + String.format("%s %s %s ", deleteLBScript, lbPublicIP, COMPARTMENT_OCID); + logger.info("Delete Load Balancer command {0}", command); + assertTrue(() -> Command.withParams( + defaultCommandParams() + .command(command1) + .redirect(true)) + .execute()); + } + } } diff --git a/integration-tests/src/test/resources/bash-scripts/delete_loadbalancer.sh b/integration-tests/src/test/resources/bash-scripts/delete_loadbalancer.sh new file mode 100755 index 00000000000..9468d606ffe --- /dev/null +++ b/integration-tests/src/test/resources/bash-scripts/delete_loadbalancer.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# +# Check if enough arguments are provided +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Assigning arguments to variables +TARGET_IP="$1" +COMPARTMENT_ID="$2" + +# List load balancers in the specified compartment +LOAD_BALANCER_OCID=$(oci lb load-balancer list --compartment-id $COMPARTMENT_ID --query "data[?contains(\"ip-addresses\"[0].\"ip-address\", '$TARGET_IP')].id | [0]" --raw-output --all) +# Check if a load balancer with the specified IP was found +if [ -z "$LOAD_BALANCER_OCID" ]; then + echo "No load balancer found with IP address: $TARGET_IP" +else + echo "Found load balancer with OCID: $LOAD_BALANCER_OCID" + # Delete the load balancer + oci lb load-balancer delete --load-balancer-id $LOAD_BALANCER_OCID --force + echo "Deletion initiated for load balancer with OCID: $LOAD_BALANCER_OCID" +fi \ No newline at end of file From 5da9a074120d33b6163ac12436dfa1076476fcd1 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Thu, 26 Sep 2024 17:57:38 +0000 Subject: [PATCH 171/356] Added support to check health status and cleanup for istio created load balancers --- .../kubernetes/ItOCILoadBalancer.java | 8 +++++-- .../extensions/InitializationTasks.java | 4 +++- .../weblogic/kubernetes/utils/IstioUtils.java | 15 +++++++++++++ .../kubernetes/utils/LoadBalancerUtils.java | 22 +++++++++++++++---- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java index da2d655f7ce..dcd00aaf60f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOCILoadBalancer.java @@ -9,6 +9,7 @@ import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.extensions.InitializationTasks; import oracle.weblogic.kubernetes.logging.LoggingFacade; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -24,6 +25,7 @@ import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndCheckForServerNameInResponse; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.createMiiDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.deleteLoadBalancer; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.getLoadBalancerIP; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyOCILoadBalancer; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; @@ -41,7 +43,7 @@ + "all managed servers in the domain through OCI Load Balancer") @IntegrationTest @Tag("oke-arm") -@Tag("oke-parallel") +@Tag("oke-gate") class ItOCILoadBalancer { // domain constants private static final int replicaCount = 2; @@ -84,6 +86,7 @@ public void tearDownAll() { if (!SKIP_CLEANUP) { Kubernetes.deleteService(OCI_LB_NAME, domainNamespace); } + deleteLoadBalancer(loadBalancerIP); } /** @@ -114,9 +117,10 @@ void testOCILoadBalancer() throws Exception { assertDoesNotThrow(() -> installAndVerifyOCILoadBalancer(domainNamespace, clusterHttpPort, clusterName, domainUid, OCI_LB_NAME), "Installation of OCI Load Balancer failed"); - loadBalancerIP = getLoadBalancerIP(domainNamespace,OCI_LB_NAME); + loadBalancerIP = getLoadBalancerIP(domainNamespace,OCI_LB_NAME, true); assertNotNull(loadBalancerIP, "External IP for Load Balancer is undefined"); logger.info("LoadBalancer IP is " + loadBalancerIP); + InitializationTasks.registerLoadBalancerExternalIP(loadBalancerIP); verifyWebAppAccessThroughOCILoadBalancer(loadBalancerIP, 2, clusterHttpPort); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 2af540f0238..afd84b2575c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -11,9 +11,11 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -134,7 +136,7 @@ public class InitializationTasks implements BeforeAllCallback, ExtensionContext. private static String wdtBasicImage; private static Collection pushedImages = new ArrayList<>(); - private static Collection lbIPs = new ArrayList<>(); + private static Set lbIPs = new HashSet<>(); private static boolean isInitializationSuccessful = false; ConditionFactory withVeryLongRetryPolicy diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index a328c271f6f..e47f2c1bfbf 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -24,6 +24,7 @@ import oracle.weblogic.domain.OnlineUpdate; import oracle.weblogic.domain.ServerPod; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; +import oracle.weblogic.kubernetes.extensions.InitializationTasks; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.SemanticVersion.Compatibility; @@ -37,6 +38,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ISTIO_VERSION; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OCNE; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CONFIG_MAP_RELOAD_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CONFIG_MAP_RELOAD_IMAGE_TAG; @@ -51,9 +53,12 @@ import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.isLoadBalancerHealthy; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -113,6 +118,16 @@ public static void installIstio() { .command(command) .redirect(false)) .execute()); + if (OKE_CLUSTER) { + String loadBalancerIP = getServiceExtIPAddrtOke("istio-ingressgateway", "istio-system"); + testUntil( + assertDoesNotThrow(() -> isLoadBalancerHealthy("istio-system", "istio-ingressgateway"), + "isLoadBalancerHealthy failed with ApiException"), + logger, + "Istio LoadBalancer to be healthy in namespace {0}", + "istio-system"); + InitializationTasks.registerLoadBalancerExternalIP(loadBalancerIP); + } } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index 5e5b67828fc..fecb68960f4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -350,9 +350,23 @@ public static Callable isLoadBalancerHealthy(String namespace, String n * @param lbName -loadbalancer service name */ public static String getLoadBalancerIP(String namespace, String lbName) throws Exception { - Map labels = new HashMap<>(); - labels.put("loadbalancer", lbName); - V1Service service = getService(lbName, null, namespace); + return getLoadBalancerIP(namespace, lbName, false); + } + + /** + * Retreive external IP from OCI LoadBalancer. + * + * @param namespace - namespace + * @param lbName -loadbalancer service name + * @param addLabel search service with label + */ + public static String getLoadBalancerIP(String namespace, String lbName, boolean addLabel) throws Exception { + Map labels = null; + if (addLabel) { + labels = new HashMap<>(); + labels.put("loadbalancer", lbName); + } + V1Service service = getService(lbName, labels, namespace); assertNotNull(service, "Can't find service with name " + lbName); LoggingFacade logger = getLogger(); logger.info("Found service with name {0} in {1} namespace ", lbName, namespace); @@ -864,7 +878,7 @@ public static String getLbExternalIp(String lbrelname, String lbns) throws Excep * @param lbPublicIP public Load Balancer IP */ public static void deleteLoadBalancer(String lbPublicIP) { - if (!lbPublicIP.isEmpty()) { + if (lbPublicIP != null && !lbPublicIP.isEmpty()) { if (lbPublicIP.startsWith("[") && lbPublicIP.endsWith("]")) { // Remove the brackets and return the content inside lbPublicIP = lbPublicIP.substring(1, lbPublicIP.length() - 1); From 586295aa1385432fe46e046fdf98bddbd7989be1 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Thu, 26 Sep 2024 20:33:39 +0000 Subject: [PATCH 172/356] Add JRF domain upgrade tests --- .../kubernetes/ItFmwDomainOnPVUpgrade.java | 589 ++++++++++++++++++ .../jrf-production-upgrade.yaml | 34 + .../jrfdomainupgrade/jrf-secure-upgrade.yaml | 38 ++ .../jrfdomainupgrade/jrfresponse.txt | 84 +++ 4 files changed, 745 insertions(+) create mode 100644 integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java create mode 100644 integration-tests/src/test/resources/jrfdomainupgrade/jrf-production-upgrade.yaml create mode 100644 integration-tests/src/test/resources/jrfdomainupgrade/jrf-secure-upgrade.yaml create mode 100644 integration-tests/src/test/resources/jrfdomainupgrade/jrfresponse.txt diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java new file mode 100644 index 00000000000..78abfcfa75e --- /dev/null +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java @@ -0,0 +1,589 @@ +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.weblogic.kubernetes; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Callable; + +import io.kubernetes.client.custom.Quantity; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; +import io.kubernetes.client.openapi.models.V1Volume; +import io.kubernetes.client.openapi.models.V1VolumeMount; +import oracle.weblogic.domain.ClusterResource; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; +import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.domain.DomainSpec; +import oracle.weblogic.domain.Opss; +import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.HelmParams; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; +import oracle.weblogic.kubernetes.annotations.IntegrationTest; +import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.assertions.impl.Cluster; +import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecResult; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.ELASTICSEARCH_HOST; +import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS; +import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; +import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_NAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.OKD; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; +import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; +import static oracle.weblogic.kubernetes.actions.ActionConstants.ITTESTS_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.TestActions.deletePod; +import static oracle.weblogic.kubernetes.actions.TestActions.execCommand; +import static oracle.weblogic.kubernetes.actions.TestActions.getDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.TestActions.imagePull; +import static oracle.weblogic.kubernetes.actions.TestActions.imageTag; +import static oracle.weblogic.kubernetes.actions.impl.Domain.shutdown; +import static oracle.weblogic.kubernetes.assertions.TestAssertions.podDoesNotExist; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; +import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; +import static oracle.weblogic.kubernetes.utils.DbUtils.startOracleDB; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; +import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; +import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; +import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; +import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResource; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createOpsswalletpasswordSecret; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test to create a FMW domain in persistent volume and upgrade it to 14.1.2.0. + */ +@DisplayName("Test to create a FMW domain in persistent volume and upgrade to 14.1.2.0") +@IntegrationTest +@Tag("kind-sequential") +@Tag("oke-sequential") +@Tag("okd-fmw-cert") +class ItFmwDomainOnPVUpgrade { + + private static String domainNamespace = null; + private static String dbNamespace = null; + + private static final String ORACLEDBURLPREFIX = "oracledb."; + private static final String RCUSYSPASSWORD = "Oradoc_db1"; + private static final String RCUSCHEMAPASSWORD = "Oradoc_db1"; + private static final String storageClassName = "fmw-domain-storage-class"; + + private static String dbUrl = null; + private static LoggingFacade logger = null; + private static String DOMAINHOMEPREFIX = null; + private static final String clusterName = "cluster-1"; + private static final int replicaCount = 2; + + private final String fmwModelFilePrefix = "model-fmwdomain-upgrade"; + + private final String imageTag1412 = "14.1.2.0.0-jdk17"; + private final String image1412 = BASE_IMAGES_PREFIX + FMWINFRA_IMAGE_NAME_DEFAULT + ":" + imageTag1412; + + /** + * Assigns unique namespaces for DB, operator and domain. + * Start DB service. + * Pull FMW image and Oracle DB image if running tests in Kind cluster. + */ + @BeforeAll + public static void initAll(@Namespaces(3) List namespaces) { + logger = getLogger(); + + // get a new unique dbNamespace + logger.info("Assign a unique namespace for DB"); + assertNotNull(namespaces.get(0), "Namespace is null"); + dbNamespace = namespaces.get(0); + final int dbListenerPort = getNextFreePort(); + dbUrl = ORACLEDBURLPREFIX + dbNamespace + ".svc.cluster.local:" + dbListenerPort + "/devpdb.k8s"; + + // get a new unique opNamespace + logger.info("Assign a unique namespace for operator"); + assertNotNull(namespaces.get(1), "Namespace is null"); + String opNamespace = namespaces.get(1); + + // get a new unique domainNamespace + logger.info("Assign a unique namespace for FMW domain"); + assertNotNull(namespaces.get(2), "Namespace is null"); + domainNamespace = namespaces.get(2); + + DOMAINHOMEPREFIX = "/shared/" + domainNamespace + "/domains/"; + + if (OKD) { + logger.info("Start DB in namespace: {0}, dbListenerPort: {1}, dbUrl: {2}, dbImage: {3}", + dbNamespace, dbListenerPort, dbUrl, DB_IMAGE_TO_USE_IN_SPEC); + assertDoesNotThrow(() -> startOracleDB(DB_IMAGE_TO_USE_IN_SPEC, getNextFreePort(), dbNamespace, dbListenerPort), + String.format("Failed to start Oracle DB in the namespace %s with dbUrl %s, dbListenerPost %s", + dbNamespace, dbUrl, dbListenerPort)); + } else { + String dbName = "fmwupgradedomain-" + "oracle-db"; + logger.info("Create Oracle DB in namespace: {0} ", dbNamespace); + createBaseRepoSecret(dbNamespace); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); + + } + // install operator and verify its running in ready state + HelmParams opHelmParams = + new HelmParams().releaseName(OPERATOR_RELEASE_NAME) + .namespace(opNamespace) + .chartDir(OPERATOR_CHART_DIR); + installAndVerifyOperator(opNamespace, opNamespace + "-sa", false, + 0, opHelmParams, ELASTICSEARCH_HOST, false, true, null, + null, false, "INFO", "DomainOnPvSimplification=true", false, domainNamespace); + + // create pull secrets for domainNamespace when running in non Kind Kubernetes cluster + // this secret is used only for non-kind cluster + createBaseRepoSecret(domainNamespace); + } + + /** + * Create a basic FMW domain on PV with server start mode prod + * Verify Pod is ready and service exists for both admin server and managed servers. + * Run the upgrade assistant to upgade the JRF domain + * verify the domain starts and is upgraded to 14.1.2.0.0 + */ + @Test + @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") + @DisplayName("Create a FMW domain and upgrade to 14.1.2.0 in prod server start mode") + void testUpgradeProductionDomain() { + String domainUid = "jrfonpv-prod"; + String domainHome = DOMAINHOMEPREFIX + domainUid; + String startMode = "prod"; + String pvcName = getUniqueName(domainUid + "-pvc-"); + String rcuSchemaPrefix = "jrfprod1"; + String fmwModelFile = Paths.get(RESOURCE_DIR, "jrfdomainupgrade", "jrf-production-upgrade.yaml").toString(); + createDomain(domainUid, startMode, rcuSchemaPrefix, fmwModelFile, pvcName); + shutdown(domainUid, domainNamespace); + launchPvHelperPod(domainNamespace, pvcName); + copyResponseFile(domainNamespace, dbUrl, rcuSchemaPrefix, domainHome); + runUpgradeAssistant(domainNamespace); + runUpgradeDomain(domainNamespace, domainHome); + deletePvhelperPod(domainNamespace); + patchDomain(domainUid, domainNamespace); + verifyDomainReady(domainUid, domainNamespace); + shutdown(domainUid, domainNamespace); + } + + /** + * Create a basic FMW domain on PV with server start mode dev + * Verify Pod is ready and service exists for both admin server and managed servers. + * Run the upgrade assistant to upgade the JRF domain + * verify the domain starts and is upgraded to 14.1.2.0.0 + */ + @Test + @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") + @DisplayName("Create a FMW domain and upgrade to 14.1.2.0 in dev server start mode") + void testUpgradeDevDomain() { + String domainUid = "jrfonpv-dev"; + String domainHome = DOMAINHOMEPREFIX + domainUid; + String startMode = "dev"; + String pvcName = getUniqueName(domainUid + "-pvc-"); + String rcuSchemaPrefix = "jrfdev1"; + String fmwModelFile = Paths.get(RESOURCE_DIR, "jrfdomainupgrade", "jrf-production-upgrade.yaml").toString(); + createDomain(domainUid, startMode, rcuSchemaPrefix, fmwModelFile, pvcName); + shutdown(domainUid, domainNamespace); + launchPvHelperPod(domainNamespace, pvcName); + copyResponseFile(domainNamespace, dbUrl, rcuSchemaPrefix, domainHome); + runUpgradeAssistant(domainNamespace); + runUpgradeDomain(domainNamespace, domainHome); + deletePvhelperPod(domainNamespace); + patchDomain(domainUid, domainNamespace); + verifyDomainReady(domainUid, domainNamespace); + shutdown(domainUid, domainNamespace); + } + + /** + * Create a basic FMW domain on PV with server start mode secure + * Verify Pod is ready and service exists for both admin server and managed servers. + * Run the upgrade assistant to upgade the JRF domain + * verify the domain starts and is upgraded to 14.1.2.0.0 + */ + @Test + @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") + @DisplayName("Create a FMW domain and upgrade to 14.1.2.0 in secure server start mode") + void testUpgradeSecureDomain() { + String domainUid = "jrfonpv-secure"; + String domainHome = DOMAINHOMEPREFIX + domainUid; + String startMode = "secure"; + String pvcName = getUniqueName(domainUid + "-pvc-"); + String rcuSchemaPrefix = "jrfsecure1"; + String fmwModelFile = Paths.get(RESOURCE_DIR, "jrfdomainupgrade", "jrf-secure-upgrade.yaml").toString(); + createDomain(domainUid, startMode, rcuSchemaPrefix, fmwModelFile, pvcName); + shutdown(domainUid, domainNamespace); + launchPvHelperPod(domainNamespace, pvcName); + copyResponseFile(domainNamespace, dbUrl, rcuSchemaPrefix, domainHome); + runUpgradeAssistant(domainNamespace); + runUpgradeDomain(domainNamespace, domainHome); + deletePvhelperPod(domainNamespace); + patchDomain(domainUid, domainNamespace); + verifyDomainReady(domainUid, domainNamespace); + shutdown(domainUid, domainNamespace); + } + + private void createDomain(String domainName, String startMode, String rcuSchemaprefix, + String fmwModelFile, String pvcName) { + + final String pvName = getUniqueName(domainName + "-pv-"); + final String wlSecretName = domainName + "-weblogic-credentials"; + // create FMW domain credential secret + createSecretWithUsernamePassword(wlSecretName, domainNamespace, + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + + // create a model property file + File fmwModelPropFile = createWdtPropertyFile(domainName, startMode, rcuSchemaprefix); + + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "dci-jrfonpv"; + String dciTag = getDateAndTimeStamp(); + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(dciTag) + .modelFiles(Collections.singletonList(fmwModelFile)) + .modelVariableFiles(Collections.singletonList(fmwModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, dciTag, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + dciTag); + + // create opss wallet password secret + String opsswalletpassSecretName = domainName + "-opss-wallet-password-secret"; + logger.info("Create OPSS wallet password secret"); + assertDoesNotThrow(() -> createOpsswalletpasswordSecret( + opsswalletpassSecretName, + domainNamespace, + ADMIN_PASSWORD_DEFAULT), + String.format("createSecret failed for %s", opsswalletpassSecretName)); + + // create a domain resource + logger.info("Creating domain custom resource"); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); + + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + this.getClass().getSimpleName()); + } + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN_AND_RCU) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.JRF) + .opss(new Opss() + .walletPasswordSecret(opsswalletpassSecretName))); + DomainResource domain = createDomainResourceOnPv( + domainName, + domainNamespace, + wlSecretName, + clusterName, + pvName, + pvcName, + new String[]{BASE_IMAGES_REPO_SECRET_NAME}, + DOMAINHOMEPREFIX, + replicaCount, + configuration, + FMWINFRA_IMAGE_TO_USE_IN_SPEC); + + // Set the inter-pod anti-affinity for the domain custom resource + setPodAntiAffinity(domain); + + // create a domain custom resource and verify domain is created + createDomainAndVerify(domain, domainNamespace); + + // verify that all servers are ready + verifyDomainReady(domainName, domainNamespace); + } + + private File createWdtPropertyFile(String domainName, String startMode, String rcuSchemaPrefix) { + + // create property file used with domain model file + Properties p = new Properties(); + p.setProperty("rcuDb", dbUrl); + p.setProperty("rcuSchemaPrefix", rcuSchemaPrefix); + p.setProperty("rcuSchemaPassword", RCUSCHEMAPASSWORD); + p.setProperty("rcuSysPassword", RCUSYSPASSWORD); + p.setProperty("adminUsername", ADMIN_USERNAME_DEFAULT); + p.setProperty("adminPassword", ADMIN_PASSWORD_DEFAULT); + p.setProperty("domainName", domainName); + p.setProperty("startMode", startMode); + + // create a model property file + File domainPropertiesFile = assertDoesNotThrow(() + -> File.createTempFile(fmwModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), + "Failed to create FMW model properties file"); + + // create the property file + assertDoesNotThrow(() + -> p.store(new FileOutputStream(domainPropertiesFile), "FMW properties file"), + "Failed to write FMW properties file"); + + return domainPropertiesFile; + } + + private Callable tagImageAndPushIfNeeded(String originalImage, String taggedImage) { + return (() -> { + boolean result = true; + imagePull(originalImage); + result = result && imageTag(originalImage, taggedImage); + imageRepoLoginAndPushImageToRegistry(taggedImage); + return result; + }); + } + + private void launchPvHelperPod(String namespace, String pvcName) { + String podName = "pvhelper"; + + String script = ITTESTS_DIR + "/../kubernetes/samples/scripts/domain-lifecycle/pv-pvc-helper.sh"; + String command = "/bin/bash " + script + " -n " + namespace + " -c " + pvcName + " -m /shared -i " + image1412; + ExecResult result = null; + try { + result = exec(new String(command), true); + getLogger().info("The command returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + assertTrue((result.exitValue() == 0), "command returned non zero value"); + } catch (Exception e) { + getLogger().info("Got exception, command failed with errors " + e.getMessage()); + } + checkPodReady(podName, null, namespace); + } + + private void copyResponseFile(String namespace, String dbUrl, String rcuSchemaPrefix, String domainHome) { + Path jrfResponseTemplateFile = Paths.get(RESOURCE_DIR, "jrfdomainupgrade", "jrfresponse.txt"); + + File jrfResponseFile = assertDoesNotThrow(() + -> File.createTempFile("jrfresponse", ".txt", new File(RESULTS_TEMPFILE)), + "Failed to create JRF upgrade assistant response file"); + assertDoesNotThrow(() -> Files.copy(jrfResponseTemplateFile, jrfResponseFile.toPath(), REPLACE_EXISTING), + "Failed to copy " + jrfResponseTemplateFile.toString()); + // replace domainHome, dbUrl and rcuSchemaPrefix in JRF response text file + assertDoesNotThrow(() -> replaceStringInFile( + jrfResponseFile.toString(), "DOMAIN_HOME", domainHome), + "Could not modify the DOMAIN_HOME in response file"); + assertDoesNotThrow(() -> replaceStringInFile( + jrfResponseFile.toString(), "DB_CONNECTION_STRING", dbUrl), + "Could not modify the DB_CONNECTION_STRING in response file"); + assertDoesNotThrow(() -> replaceStringInFile( + jrfResponseFile.toString(), "RCU_SCHEMA_PREFIX", rcuSchemaPrefix), + "Could not modify the RCU_SCHEMA_PREFIX in response file"); + assertDoesNotThrow(() -> copyFileToPod(namespace, + "pvhelper", "", + jrfResponseFile.toPath(), + Paths.get("/tmp/jrfresponse.txt")), + "Copying file to pod failed"); + } + + private void runUpgradeAssistant(String namespace) { + String command = "/u01/oracle/oracle_common/upgrade/bin/ua -response /tmp/jrfresponse.txt -logDir /tmp"; + String success = "Oracle Metadata Services schema upgrade finished with status: succeeded"; + try { + ExecResult result = execCommand(namespace, "pvhelper", null, true, "/bin/sh", "-c", command); + boolean done = result.stderr().contains(success) || result.stdout().contains(success); + assertTrue(done, "upgrade assistant didn't succeed"); + } catch (ApiException | IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + } + + private void runUpgradeDomain(String namespace, String domainHome) { + try { + List stringsToWrite = Arrays.asList( + "readDomainForUpgrade(\"" + domainHome + "\")", + "updateDomain()", + "closeDomain()"); + File wlstScript = assertDoesNotThrow(() + -> File.createTempFile("upgradeDomain", ".py", new File(RESULTS_TEMPFILE)), + "Failed to create JRF upgrade domain python script file"); + Files.write(wlstScript.toPath(), stringsToWrite, StandardOpenOption.CREATE); + logger.info("Strings written to the file successfully!"); + assertDoesNotThrow(() -> copyFileToPod(namespace, + "pvhelper", "", + wlstScript.toPath(), + Paths.get("/tmp/" + wlstScript.getName())), + "Copying file to pod failed"); + String command = "/u01/oracle/oracle_common/common/bin/wlst.sh /tmp/" + wlstScript.getName(); + + ExecResult result = execCommand(namespace, "pvhelper", null, true, "/bin/sh", "-c", command); + assertTrue((result.exitValue() == 0), "command returned non zero value"); + } catch (ApiException | IOException | InterruptedException ex) { + logger.severe(ex.getMessage()); + } + } + + private void deletePvhelperPod(String namespace) { + assertDoesNotThrow(() -> deletePod("pvhelper", namespace)); + testUntil( + podDoesNotExist("pvhelper", null, namespace), + logger, + "{0} to be deleted in namespace {1}", + "pvhelper", + namespace); + } + + private void patchDomain(String domainUid, String namespace) { + DomainResource domain = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, namespace), + String.format("getDomainCustomResource failed with ApiException when tried to get domain %s in namespace %s", + domainUid, namespace)); + assertNotNull(domain, "Got null domain resource"); + logger.info("image before update: " + domain.getSpec().getImage()); + + String patchStr = "[" + + "{\"op\": \"replace\", \"path\": \"/spec/image\", \"value\": \"" + image1412 + "\"}," + + "{\"op\": \"replace\", \"path\": \"/spec/serverStartPolicy\", \"value\": \"IfNeeded\"}" + + "]"; + logger.info("PatchStr for imageUpdate: {0}", patchStr); + + assertTrue(patchDomainResource(domainUid, namespace, new StringBuffer(patchStr)), + "patchDomainCustomResource(imageUpdate) failed"); + + domain = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, namespace), + String.format("getDomainCustomResource failed with ApiException when tried to get domain %s in namespace %s", + domainUid, namespace)); + assertNotNull(domain, "Got null domain resource after patching"); + logger.info("image after update: " + domain.getSpec().getImage()); + } + + private void verifyDomainReady(String domainUid, String namespace) { + String adminServerPodName = domainUid + "-admin-server"; + // verify the admin server service and pod created + checkPodReadyAndServiceExists(adminServerPodName, domainUid, namespace); + + String cluster1ManagedServerPodNamePrefix = domainUid + "-managed-server"; + // verify managed server services and pods are created + for (int i = 1; i <= 2; i++) { + logger.info("Checking managed server service and pod {0} is created in namespace {1}", + cluster1ManagedServerPodNamePrefix + i, namespace); + checkPodReadyAndServiceExists(cluster1ManagedServerPodNamePrefix + i, domainUid, namespace); + } + } + + private static DomainResource createDomainResourceOnPv(String domainUid, + String domNamespace, + String adminSecretName, + String clusterName, + String pvName, + String pvcName, + String[] repoSecretName, + String domainInHomePrefix, + int replicaCount, + Configuration configuration, + String imageToUse) { + + // create secrets + List secrets = new ArrayList<>(); + for (String secret : repoSecretName) { + secrets.add(new V1LocalObjectReference().name(secret)); + } + + // create a domain custom resource configuration object + DomainResource domain = new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new V1ObjectMeta() + .name(domainUid) + .namespace(domNamespace)) + .spec(new DomainSpec() + .domainUid(domainUid) + .domainHome(domainInHomePrefix + domainUid) + .domainHomeSourceType("PersistentVolume") + .image(imageToUse) + .imagePullPolicy(IMAGE_PULL_POLICY) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(adminSecretName)) + .includeServerOutInPodLog(true) + .logHomeEnabled(Boolean.TRUE) + .logHome("/shared/" + domNamespace + "/logs/" + domainUid) + .dataHome("") + .serverStartPolicy("IfNeeded") + .failureRetryIntervalSeconds(FAILURE_RETRY_INTERVAL_SECONDS) + .failureRetryLimitMinutes(FAILURE_RETRY_LIMIT_MINUTES) + .serverPod(new ServerPod() //serverpod + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false " + + "-Dweblogic.security.SSL.ignoreHostnameVerification=true")) + .addEnvItem(new V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom")) + .addVolumesItem(new V1Volume() + .name(pvName) + .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource() + .claimName(pvcName))) + .addVolumeMountsItem(new V1VolumeMount() + .mountPath("/shared") + .name(pvName))) + .configuration(configuration)); + domain.spec().setImagePullSecrets(secrets); + + // create cluster resource for the domain + String clusterResName = domainUid + "-" + clusterName; + if (!Cluster.doesClusterExist(clusterResName, CLUSTER_VERSION, domNamespace)) { + ClusterResource cluster = createClusterResource(clusterResName, + clusterName, domNamespace, replicaCount); + createClusterAndVerify(cluster); + } + domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterResName)); + + return domain; + } + +} diff --git a/integration-tests/src/test/resources/jrfdomainupgrade/jrf-production-upgrade.yaml b/integration-tests/src/test/resources/jrfdomainupgrade/jrf-production-upgrade.yaml new file mode 100644 index 00000000000..53191f77eb7 --- /dev/null +++ b/integration-tests/src/test/resources/jrfdomainupgrade/jrf-production-upgrade.yaml @@ -0,0 +1,34 @@ +# Copyright (c) 2023, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@PROP:adminUsername@@' + AdminPassword: '@@PROP:adminPassword@@' + ServerStartMode: '@@PROP:startMode@@' + RCUDbInfo: + rcu_prefix: '@@PROP:rcuSchemaPrefix@@' + rcu_schema_password: '@@PROP:rcuSchemaPassword@@' + rcu_db_conn_string: '@@PROP:rcuDb@@' + rcu_admin_password: '@@PROP:rcuSysPassword@@' + +topology: + AdminServerName: "admin-server" + Name: '@@PROP:domainName@@' + Log: + FileName: '@@PROP:domainName@@.log' + Cluster: + "cluster-1": + FrontendHost: '@@PROP:domainName@@-cluster-cluster-1' + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "managed-server" + DynamicClusterSize: 5 + MaxDynamicClusterSize: 5 + CalculatedListenPorts: false + Server: + "admin-server": + ListenPort: 7001 + ServerTemplate: + "cluster-1-template": + Cluster: "cluster-1" + ListenPort : 8001 diff --git a/integration-tests/src/test/resources/jrfdomainupgrade/jrf-secure-upgrade.yaml b/integration-tests/src/test/resources/jrfdomainupgrade/jrf-secure-upgrade.yaml new file mode 100644 index 00000000000..f87e975b3c3 --- /dev/null +++ b/integration-tests/src/test/resources/jrfdomainupgrade/jrf-secure-upgrade.yaml @@ -0,0 +1,38 @@ +# Copyright (c) 2023, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@PROP:adminUsername@@' + AdminPassword: '@@PROP:adminPassword@@' + ServerStartMode: '@@PROP:startMode@@' + RCUDbInfo: + rcu_prefix: '@@PROP:rcuSchemaPrefix@@' + rcu_schema_password: '@@PROP:rcuSchemaPassword@@' + rcu_db_conn_string: '@@PROP:rcuDb@@' + rcu_admin_password: '@@PROP:rcuSysPassword@@' + +topology: + Name: '@@PROP:domainName@@' + Log: + FileName: '@@PROP:domainName@@.log' + AdminServerName: "admin-server" + Server: + admin-server: + SSL: + Enabled: true + ListenPort: 7002 + Cluster: + "cluster-1": + FrontendHost: '@@PROP:domainName@@-cluster-cluster-1' + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "managed-server" + DynamicClusterSize: 5 + MaxDynamicClusterSize: 5 + CalculatedListenPorts: false + ServerTemplate: + cluster-1-template: + Cluster: cluster-1 + SSL: + Enabled: true + ListenPort: 8002 diff --git a/integration-tests/src/test/resources/jrfdomainupgrade/jrfresponse.txt b/integration-tests/src/test/resources/jrfdomainupgrade/jrfresponse.txt new file mode 100644 index 00000000000..27561965093 --- /dev/null +++ b/integration-tests/src/test/resources/jrfdomainupgrade/jrfresponse.txt @@ -0,0 +1,84 @@ +[GENERAL] +fileFormatVersion = 3 + +# The next section contains information for accessing a WebLogic Server domain. + +[UAWLSINTERNAL.UAWLS] +pluginInstance = 1 + +# Specifies the WebLogic Server domain directory: +UASVR.path = DOMAIN_HOME + +# Do not change the next line. +[OPSS.OPSS_SCHEMA_PLUGIN] +pluginInstance = 10 + +OPSS.databaseType = Oracle Database + +# Specifies the database connection string for the DBA user. +# The format depends upon the database type. + +OPSS.databaseConnectionString = DB_CONNECTION_STRING + +# Specifies the database connection string for the user schema. +# The format depends upon the database type. + +OPSS.schemaConnectionString = DB_CONNECTION_STRING + +# Specifies the name of the schema or database user + +#OPSS.schemaUserName = FMWTEST_OPSS +OPSS.schemaUserName = RCU_SCHEMA_PREFIX_OPSS + +# Specifies the password for the schema, in encrypted form. +# To specify a different password in cleartext, use the "cleartextSchemaPassword" keyword instead: + +OPSS.cleartextSchemaPassword = Oradoc_db1 + +# encrypted password can be generated with command line option -createResponse +#OPSS.encryptedSchemaPassword = 0551CF2EACFC4FE7BCB1F860FCF68E13AA6E61A724E7CFC09E +# Specifies the name of the database administrator account. + +OPSS.dbaUserName = sys as sysdba + +# Specifies the password for the database administrator account, in encrypted form. +# To specify a different password in cleartext, use the "cleartextDbaPassword" keyword +# instead: + +OPSS.cleartextDbaPassword = Oradoc_db1 + +#OPSS.encryptedDbaPassword = 057B3698F71FB2EE583D32EF36234174DCC2C7276FC11F77E7 + +[MDS.SCHEMA_UPGRADE] +pluginInstance = 11 + +MDS.databaseConnectionString = DB_CONNECTION_STRING +MDS.schemaConnectionString = DB_CONNECTION_STRING +MDS.schemaUserName = RCU_SCHEMA_PREFIX_MDS +MDS.cleartextSchemaPassword = Oradoc_db1 +MDS.dbaUserName = sys as sysdba +MDS.cleartextDbaPassword = Oradoc_db1 + +[IAU.AUDIT_SCHEMA_PLUGIN] +pluginInstance = 6 + +IAU.databaseType = Oracle Database +IAU.databaseConnectionString = DB_CONNECTION_STRING +IAU.schemaConnectionString = DB_CONNECTION_STRING +IAU.schemaUserName = RCU_SCHEMA_PREFIX_IAU +IAU.cleartextSchemaPassword = Oradoc_db1 +IAU.dbaUserName = sys as sysdba +IAU.cleartextDbaPassword = Oradoc_db1 + + +[FMWCONFIG.CIE_SCHEMA_PLUGIN] +pluginInstance = 4 + +STB.databaseType = Oracle Database +STB.databaseConnectionString = DB_CONNECTION_STRING +STB.schemaConnectionString = DB_CONNECTION_STRING +STB.schemaUserName = RCU_SCHEMA_PREFIX_STB +STB.cleartextSchemaPassword = Oradoc_db1 +STB.dbaUserName = sys as sysdba +STB.cleartextDbaPassword = Oradoc_db1 + From 7c4306a3c2d372c18bd9b9a54ef21460914ed5d0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 27 Sep 2024 18:24:16 +0000 Subject: [PATCH 173/356] Backport of correction to processing of pod and container securityContext --- documentation/domains/Cluster.json | 4 +- documentation/domains/Cluster.md | 4 +- documentation/domains/Domain.json | 6 +- documentation/domains/Domain.md | 6 +- .../site/content/faq/resource-settings.md | 4 +- .../domain-security/pod-and-container.md | 2 +- kubernetes/crd/cluster-crd.yaml | 15 +- kubernetes/crd/domain-crd.yaml | 51 +- .../java/oracle/kubernetes/operator/Pair.java | 14 +- .../operator/helpers/JobStepContext.java | 2 +- .../operator/helpers/PodStepContext.java | 96 +- .../domain/model/IntrospectorJobPod.java | 64 +- .../weblogic/domain/model/ServerPod.java | 134 +- .../operator/helpers/AdminPodHelperTest.java | 25 - .../operator/helpers/JobHelperTest.java | 11 +- .../helpers/ManagedPodHelperTest.java | 25 - .../operator/helpers/PodHelperTestBase.java | 80 -- .../operator/helpers/ReferenceObjects.java | 1208 +---------------- 18 files changed, 225 insertions(+), 1526 deletions(-) diff --git a/documentation/domains/Cluster.json b/documentation/domains/Cluster.json index 556206bdec3..52f5e27da5e 100644 --- a/documentation/domains/Cluster.json +++ b/documentation/domains/Cluster.json @@ -184,7 +184,7 @@ "type": "string" }, "podSecurityContext": { - "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", + "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext" }, "priorityClassName": { @@ -239,7 +239,7 @@ "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe" }, "containerSecurityContext": { - "description": "Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", + "description": "Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. If no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext" }, "envFrom": { diff --git a/documentation/domains/Cluster.md b/documentation/domains/Cluster.md index 43bb35d7b99..480816a6e54 100644 --- a/documentation/domains/Cluster.md +++ b/documentation/domains/Cluster.md @@ -56,7 +56,7 @@ The specification of the operation of the WebLogic cluster. Required. | `affinity` | [Affinity](k8s1.28.2.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/. See `kubectl explain pods.spec.affinity`. | | `annotations` | Map | The annotations to be added to generated resources. | | `containers` | Array of [Container](k8s1.28.2.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. | -| `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | +| `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. If no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. | | `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. | | `hostAliases` | Array of [Host Alias](k8s1.28.2.md#host-alias) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | @@ -67,7 +67,7 @@ The specification of the operation of the WebLogic cluster. Required. | `maxReadyWaitTimeSeconds` | integer | The maximum time in seconds that the operator waits for a WebLogic Server pod to reach the ready state before it considers the pod failed. Defaults to 1800 seconds. | | `nodeName` | string | NodeName is a request to schedule this Pod onto a specific Node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits the resource requirements. See `kubectl explain pods.spec.nodeName`. | | `nodeSelector` | Map | Selector which must match a Node's labels for the Pod to be scheduled on that Node. See `kubectl explain pods.spec.nodeSelector`. | -| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | +| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `priorityClassName` | string | If specified, indicates the Pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be the default or zero, if there is no default. See `kubectl explain pods.spec.priorityClassName`. | | `readinessGates` | Array of [Pod Readiness Gate](k8s1.28.2.md#pod-readiness-gate) | If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to "True". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md. | | `readinessProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access. | diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index 3528e55f5d6..5217e91d0f6 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -761,7 +761,7 @@ "type": "object", "properties": { "podSecurityContext": { - "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", + "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext" }, "resources": { @@ -1045,7 +1045,7 @@ "type": "string" }, "podSecurityContext": { - "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", + "description": "Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.PodSecurityContext" }, "priorityClassName": { @@ -1100,7 +1100,7 @@ "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe" }, "containerSecurityContext": { - "description": "Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", + "description": "Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. If no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext" }, "envFrom": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 35fa0d3c8d3..2ba3641c9c3 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -157,7 +157,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | `affinity` | [Affinity](k8s1.28.2.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/. See `kubectl explain pods.spec.affinity`. | | `annotations` | Map | The annotations to be added to generated resources. | | `containers` | Array of [Container](k8s1.28.2.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. | -| `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | +| `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. If no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. | | `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the container running a WebLogic Server instance. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. | | `hostAliases` | Array of [Host Alias](k8s1.28.2.md#host-alias) | HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods. | @@ -168,7 +168,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | `maxReadyWaitTimeSeconds` | integer | The maximum time in seconds that the operator waits for a WebLogic Server pod to reach the ready state before it considers the pod failed. Defaults to 1800 seconds. | | `nodeName` | string | NodeName is a request to schedule this Pod onto a specific Node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits the resource requirements. See `kubectl explain pods.spec.nodeName`. | | `nodeSelector` | Map | Selector which must match a Node's labels for the Pod to be scheduled on that Node. See `kubectl explain pods.spec.nodeSelector`. | -| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | +| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `priorityClassName` | string | If specified, indicates the Pod's priority. "system-node-critical" and "system-cluster-critical" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be the default or zero, if there is no default. See `kubectl explain pods.spec.priorityClassName`. | | `readinessGates` | Array of [Pod Readiness Gate](k8s1.28.2.md#pod-readiness-gate) | If specified, all readiness gates will be evaluated for Pod readiness. A Pod is ready when all its containers are ready AND all conditions specified in the readiness gates have a status equal to "True". More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md. | | `readinessProbe` | [Probe](k8s1.28.2.md#probe) | Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access. | @@ -278,7 +278,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | --- | --- | --- | | `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the Introspector Job Pod container. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. | | `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the Introspector Job Pod container. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. | -| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | +| `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Introspector Job Pod. See `kubectl explain pods.spec.containers.resources`. | ### Shutdown diff --git a/documentation/site/content/faq/resource-settings.md b/documentation/site/content/faq/resource-settings.md index 9202a8413a1..c152b12b51a 100644 --- a/documentation/site/content/faq/resource-settings.md +++ b/documentation/site/content/faq/resource-settings.md @@ -44,7 +44,7 @@ By default, the introspector job pod uses the same CPU and memory settings as th domain's WebLogic Administration Server pod. Similarly, the operator created init containers in the introspector job pod for the [Auxiliary Images]({{< relref "/managing-domains/model-in-image/auxiliary-images" >}}) based domains use the same CPU and memory settings as the domain's WebLogic Administration Server pod. -Beginning with operator version 4.0.5, you can override the settings of the introspector job pod using +You can override the settings of the introspector job pod using the `domain.spec.introspector.serverPod` element. A resource request sets the minimum amount of a resource that a container requires. A resource limit is the maximum amount of a resource a container is given @@ -63,7 +63,7 @@ and you can override the setting for individual WebLogic Server instances using setting for member servers of a cluster using the `cluster.spec.serverPod` element. Note that the introspector job pod uses the same settings as the WebLogic Administration Server pod. -Beginning with operator version 4.0.5, you can override the settings of the introspector job pod +You can override the settings of the introspector job pod using the `domain.spec.introspector.serverPod` element. Values set in the `.serverPod` stanzas for a more specific type of pod, override diff --git a/documentation/site/content/security/domain-security/pod-and-container.md b/documentation/site/content/security/domain-security/pod-and-container.md index 6217afb77c7..032c63eb659 100644 --- a/documentation/site/content/security/domain-security/pod-and-container.md +++ b/documentation/site/content/security/domain-security/pod-and-container.md @@ -9,7 +9,7 @@ The WebLogic Kubernetes Operator [enforces pod and container security best pract for the pods and containers that the operator creates for WebLogic Server instances, the init container for auxiliary images, sidecar containers for Fluentd or the WebLogic Monitoring Exporter, and the introspection job. -Beginning with operator version 4.0.5, the operator adds the following pod-level `securityContext` content: +The operator adds the following pod-level `securityContext` content: ```yaml securityContext: diff --git a/kubernetes/crd/cluster-crd.yaml b/kubernetes/crd/cluster-crd.yaml index aea21d23ed4..4157548288e 100644 --- a/kubernetes/crd/cluster-crd.yaml +++ b/kubernetes/crd/cluster-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 2f9c1cd88595595fecef563dbc9a149f318eab7146b63cb6142c45279ad4f9e7 + weblogic.sha256: 34ed6e0ff57580da665db440dce607569f175d0d93934c0e31b2ab26e7ede28d name: clusters.weblogic.oracle spec: group: weblogic.oracle @@ -116,10 +116,9 @@ spec: type: string podSecurityContext: description: 'Pod-level security attributes. See `kubectl explain - pods.spec.securityContext`. Beginning with operator version - 4.0.5, if no value is specified for this field, the operator - will use default content for the pod-level `securityContext`. - More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + pods.spec.securityContext`. If no value is specified for this + field, the operator will use default content for the pod-level + `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: runAsUser: type: integer @@ -407,9 +406,9 @@ spec: containerSecurityContext: description: 'Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. - Beginning with operator version 4.0.5, if no value is specified - for this field, the operator will use default content for container-level - `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + If no value is specified for this field, the operator will use + default content for container-level `securityContext`. More + info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: privileged: type: boolean diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 52a019ec048..c468105717c 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 71f7e932ff00465bdd41107cd2f1c5c8d16d658ba6787269b714574e62397b71 + weblogic.sha256: 3cad99b24fb84de65dc38d7734ce269fb7058c3b6aed32b85ef590e142921635 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -1210,10 +1210,9 @@ spec: type: string podSecurityContext: description: 'Pod-level security attributes. See `kubectl - explain pods.spec.securityContext`. Beginning with operator - version 4.0.5, if no value is specified for this field, - the operator will use default content for the pod-level - `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + explain pods.spec.securityContext`. If no value is specified + for this field, the operator will use default content for + the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: runAsUser: type: integer @@ -1504,10 +1503,9 @@ spec: containerSecurityContext: description: 'Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain - pods.spec.containers.securityContext`. Beginning with operator - version 4.0.5, if no value is specified for this field, - the operator will use default content for container-level - `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + pods.spec.containers.securityContext`. If no value is specified + for this field, the operator will use default content for + container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: privileged: type: boolean @@ -4144,10 +4142,9 @@ spec: properties: podSecurityContext: description: 'Pod-level security attributes. See `kubectl - explain pods.spec.securityContext`. Beginning with operator - version 4.0.5, if no value is specified for this field, - the operator will use default content for the pod-level - `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + explain pods.spec.securityContext`. If no value is specified + for this field, the operator will use default content for + the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: runAsUser: type: integer @@ -4459,10 +4456,9 @@ spec: type: string podSecurityContext: description: 'Pod-level security attributes. See `kubectl explain - pods.spec.securityContext`. Beginning with operator version - 4.0.5, if no value is specified for this field, the operator - will use default content for the pod-level `securityContext`. - More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + pods.spec.securityContext`. If no value is specified for this + field, the operator will use default content for the pod-level + `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: runAsUser: type: integer @@ -4750,9 +4746,9 @@ spec: containerSecurityContext: description: 'Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. - Beginning with operator version 4.0.5, if no value is specified - for this field, the operator will use default content for container-level - `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + If no value is specified for this field, the operator will use + default content for container-level `securityContext`. More + info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' properties: privileged: type: boolean @@ -7347,10 +7343,9 @@ spec: type: string podSecurityContext: description: 'Pod-level security attributes. See `kubectl - explain pods.spec.securityContext`. Beginning with operator - version 4.0.5, if no value is specified for this field, - the operator will use default content for the pod-level - `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + explain pods.spec.securityContext`. If no value is specified + for this field, the operator will use default content + for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' type: object properties: runAsUser: @@ -7643,10 +7638,10 @@ spec: containerSecurityContext: description: 'Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl - explain pods.spec.containers.securityContext`. Beginning - with operator version 4.0.5, if no value is specified - for this field, the operator will use default content - for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' + explain pods.spec.containers.securityContext`. If no value + is specified for this field, the operator will use default + content for container-level `securityContext`. More info: + https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.' type: object properties: privileged: diff --git a/operator/src/main/java/oracle/kubernetes/operator/Pair.java b/operator/src/main/java/oracle/kubernetes/operator/Pair.java index a2caed25f4f..fada23b5b30 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/Pair.java +++ b/operator/src/main/java/oracle/kubernetes/operator/Pair.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. +// Copyright (c) 2019, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -9,6 +9,18 @@ public record Pair(L left, R right) { + /** + * Create pair. + * @param Left type + * @param Right type + * @param left left + * @param right right + * @return pair + */ + public static Pair of(A left, B right) { + return new Pair(left, right); + } + @Override public String toString() { return new ToStringBuilder(this).append("left", left).append("right", right).toString(); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index f4baad65253..351ff2bd714 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -503,7 +503,7 @@ V1SecurityContext getInitContainerSecurityContext() { if (isInitDomainOnPVRunAsRoot()) { return new V1SecurityContext().runAsGroup(0L).runAsUser(0L); } - if (getPodSecurityContext().equals(new V1PodSecurityContext())) { + if (getPodSecurityContext().equals(PodSecurityHelper.getDefaultPodSecurityContext())) { return PodSecurityHelper.getDefaultContainerSecurityContext(); } return creatSecurityContextFromPodSecurityContext(getPodSecurityContext()); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index 32569905624..a5fec121c83 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -14,7 +14,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.stream.IntStream; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -38,12 +37,14 @@ import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Pod; import io.kubernetes.client.openapi.models.V1PodReadinessGate; +import io.kubernetes.client.openapi.models.V1PodSecurityContext; import io.kubernetes.client.openapi.models.V1PodSpec; import io.kubernetes.client.openapi.models.V1PodSpecBuilder; import io.kubernetes.client.openapi.models.V1Probe; import io.kubernetes.client.openapi.models.V1ProbeBuilder; import io.kubernetes.client.openapi.models.V1ResourceRequirements; import io.kubernetes.client.openapi.models.V1SecretVolumeSource; +import io.kubernetes.client.openapi.models.V1SecurityContext; import io.kubernetes.client.openapi.models.V1Volume; import io.kubernetes.client.openapi.models.V1VolumeMount; import io.kubernetes.client.util.Yaml; @@ -57,6 +58,7 @@ import oracle.kubernetes.operator.LabelConstants; import oracle.kubernetes.operator.LogHomeLayoutType; import oracle.kubernetes.operator.MIINonDynamicChangesMethod; +import oracle.kubernetes.operator.Pair; import oracle.kubernetes.operator.PodAwaiterStepFactory; import oracle.kubernetes.operator.ProcessingConstants; import oracle.kubernetes.operator.WebLogicConstants; @@ -504,14 +506,6 @@ private boolean isRecentOperator(String operatorVersion) { } } - private void addLegacyPrometheusAnnotationsFrom31(V1Pod pod) { - AnnotationHelper.annotateForPrometheus(pod.getMetadata(), WLS_EXPORTER, getMetricsPort()); - } - - private Integer getMetricsPort() { - return getListenPort() != null ? getListenPort() : getSslListenPort(); - } - private String getLabel(V1Pod currentPod, String key) { return currentPod.getMetadata().getLabels().get(key); } @@ -1152,19 +1146,11 @@ private boolean hasLabel(V1Pod pod, String key) { return pod.getMetadata().getLabels().containsKey(key); } - private boolean isLegacyPod(V1Pod currentPod) { - return !hasLabel(currentPod, OPERATOR_VERSION); - } - private boolean isPodFromRecentOperator(V1Pod currentPod) { return Optional.ofNullable(currentPod.getMetadata()).map(V1ObjectMeta::getLabels) .map(l -> l.get(OPERATOR_VERSION)).map(PodStepContext.this::isRecentOperator).orElse(false); } - private boolean isLegacyMiiPod(V1Pod currentPod) { - return hasLabel(currentPod, MODEL_IN_IMAGE_DOMAINZIP_HASH); - } - private void setLabel(V1Pod currentPod, String key, String value) { currentPod.getMetadata().putLabelsItem(key, value); } @@ -1174,37 +1160,13 @@ private void copyLabel(V1Pod fromPod, V1Pod toPod, String key) { setLabel(toPod, key, getLabel(fromPod, key)); } - private String adjustedLegacyHash(V1Pod currentPod, Consumer adjustment) { + private String adjustedHash(V1Pod currentPod, List>> adjustments) { V1Pod recipe = createPodRecipe(); - adjustment.accept(recipe); - - if (isLegacyMiiPod(currentPod)) { - copyLabel(currentPod, recipe, MODEL_IN_IMAGE_DOMAINZIP_HASH); - } - - restoreAffinityContent(recipe, currentPod); + adjustments.forEach(adjustment -> adjustment.right().accept(recipe, currentPod)); return AnnotationHelper.createHash(recipe); } - private String adjustedHash(V1Pod currentPod, List> adjustments) { - V1Pod recipe = createPodRecipe(); - adjustments.forEach(adjustment -> adjustment.accept(recipe, currentPod)); - - return AnnotationHelper.createHash(recipe); - } - - private void addLegacyPrometheusAnnotationsFrom30(V1Pod pod) { - AnnotationHelper.annotateForPrometheus(pod.getMetadata(), WLS_EXPORTER, getOldMetricsPort()); - } - - private boolean canAdjustLegacyHashToMatch(V1Pod currentPod, String requiredHash) { - // Legacy pods could be created by operator version 3.0 or 3.1 - return requiredHash.equals(adjustedLegacyHash(currentPod, this::addLegacyPrometheusAnnotationsFrom30)) - || requiredHash.equals( - adjustedLegacyHash(currentPod, PodStepContext.this::addLegacyPrometheusAnnotationsFrom31)); - } - private void adjustVolumeMountName(List convertedVolumeMounts, V1VolumeMount volumeMount) { convertedVolumeMounts.add(volumeMount.name(volumeMount.getName().replaceAll("^" + COMPATIBILITY_MODE + AUXILIARY_IMAGE_VOLUME_NAME_PREFIX, AUXILIARY_IMAGE_VOLUME_NAME_OLD_PREFIX))); @@ -1382,31 +1344,53 @@ private void restoreSecurityContext(V1Pod recipe, V1Pod currentPod) { })); } + private void restoreSecurityContextEmpty(V1Pod recipe, V1Pod currentPod) { + if (PodSecurityHelper.getDefaultPodSecurityContext().equals(recipe.getSpec().getSecurityContext())) { + recipe.getSpec().setSecurityContext(new V1PodSecurityContext()); + } + Optional.ofNullable(recipe.getSpec().getContainers()) + .ifPresent(containers -> containers.forEach(container -> { + if (PodSecurityHelper.getDefaultContainerSecurityContext().equals(container.getSecurityContext())) { + container.setSecurityContext( + getContainerName().equals(container.getName()) ? new V1SecurityContext() : null); + } + })); + } + + private void restoreSecurityContextEmptyInitContainer(V1Pod recipe, V1Pod currentPod) { + Optional.ofNullable(recipe.getSpec().getInitContainers()) + .ifPresent(initContainers -> initContainers.forEach(initContainer -> { + if (PodSecurityHelper.getDefaultContainerSecurityContext().equals(initContainer.getSecurityContext())) { + initContainer.setSecurityContext(isAuxiliaryContainer(initContainer) ? null : new V1SecurityContext()); + } + })); + } + private boolean canAdjustRecentOperatorMajorVersion3HashToMatch(V1Pod currentPod, String requiredHash) { // start with list of adjustment methods // generate stream of combinations // for each combination, start with pod recipe, apply all adjustments, and generate hash // return true if any adjusted hash matches required hash - List> adjustments = List.of( - this::restoreMetricsExporterSidecarPortTcpMetrics, - this::convertAuxImagesInitContainerVolumeAndMounts, - this::restoreLegacyIstioPortsConfig, - this::restoreAffinityContent, - this::restoreLogHomeLayoutEnvVar, - this::restoreFluentdVolume, - this::restoreSecurityContext); + List>> adjustments = List.of( + Pair.of("restoreMetricsExporterSidecarPortTcpMetrics", this::restoreMetricsExporterSidecarPortTcpMetrics), + Pair.of("convertAuxImagesInitContainerVolumeAndMounts", + this::convertAuxImagesInitContainerVolumeAndMounts), + Pair.of("restoreLegacyIstioPortsConfig", this::restoreLegacyIstioPortsConfig), + Pair.of("restoreAffinityContent", this::restoreAffinityContent), + Pair.of("restoreLogHomeLayoutEnvVar", this::restoreLogHomeLayoutEnvVar), + Pair.of("restoreFluentdVolume", this::restoreFluentdVolume), + Pair.of("restoreSecurityContext", this::restoreSecurityContext), + Pair.of("restoreSecurityContextEmpty", this::restoreSecurityContextEmpty), + Pair.of("restoreSecurityContextEmptyInitContainer", this::restoreSecurityContextEmptyInitContainer)); return Combinations.of(adjustments) .map(adjustment -> adjustedHash(currentPod, adjustment)) .anyMatch(requiredHash::equals); } private boolean hasCorrectPodHash(V1Pod currentPod) { - - return (isLegacyPod(currentPod) - && canAdjustLegacyHashToMatch(currentPod, AnnotationHelper.getHash(currentPod))) + return AnnotationHelper.getHash(getPodModel()).equals(AnnotationHelper.getHash(currentPod)) || (isPodFromRecentOperator(currentPod) - && canAdjustRecentOperatorMajorVersion3HashToMatch(currentPod, AnnotationHelper.getHash(currentPod))) - || AnnotationHelper.getHash(getPodModel()).equals(AnnotationHelper.getHash(currentPod)); + && canAdjustRecentOperatorMajorVersion3HashToMatch(currentPod, AnnotationHelper.getHash(currentPod))); } private boolean canUseCurrentPod(V1Pod currentPod) { diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java index be7e69d2ef9..d3aa4fad213 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -69,10 +69,10 @@ class IntrospectorJobPod { * */ @Description("Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. " - + "Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default " + + "If no value is specified for this field, the operator will use default " + "content for the pod-level `securityContext`. " + "More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.") - private V1PodSecurityContext podSecurityContext = new V1PodSecurityContext(); + private V1PodSecurityContext podSecurityContext = null; private static void copyValues(V1ResourceRequirements to, V1ResourceRequirements from) { if (from != null) { @@ -85,27 +85,41 @@ private static void copyValues(V1ResourceRequirements to, V1ResourceRequirements } } - void copyValues(V1PodSecurityContext to, V1PodSecurityContext from) { - if (to.getRunAsNonRoot() == null) { - to.runAsNonRoot(from.getRunAsNonRoot()); - } - if (to.getFsGroup() == null) { - to.fsGroup(from.getFsGroup()); - } - if (to.getRunAsGroup() == null) { - to.runAsGroup(from.getRunAsGroup()); - } - if (to.getRunAsUser() == null) { - to.runAsUser(from.getRunAsUser()); - } - if (to.getSeLinuxOptions() == null) { - to.seLinuxOptions(from.getSeLinuxOptions()); - } - if (to.getSupplementalGroups() == null) { - to.supplementalGroups(from.getSupplementalGroups()); - } - if (to.getSysctls() == null) { - to.sysctls(from.getSysctls()); + private void copyValues(V1PodSecurityContext from) { + if (from != null) { + if (podSecurityContext == null) { + podSecurityContext = new V1PodSecurityContext(); + } + if (podSecurityContext.getRunAsNonRoot() == null) { + podSecurityContext.runAsNonRoot(from.getRunAsNonRoot()); + } + if (podSecurityContext.getFsGroup() == null) { + podSecurityContext.fsGroup(from.getFsGroup()); + } + if (podSecurityContext.getRunAsGroup() == null) { + podSecurityContext.runAsGroup(from.getRunAsGroup()); + } + if (podSecurityContext.getRunAsUser() == null) { + podSecurityContext.runAsUser(from.getRunAsUser()); + } + if (podSecurityContext.getSeLinuxOptions() == null) { + podSecurityContext.seLinuxOptions(from.getSeLinuxOptions()); + } + if (podSecurityContext.getSupplementalGroups() == null) { + podSecurityContext.supplementalGroups(from.getSupplementalGroups()); + } + if (podSecurityContext.getSysctls() == null) { + podSecurityContext.sysctls(from.getSysctls()); + } + if (podSecurityContext.getFsGroupChangePolicy() == null) { + podSecurityContext.fsGroupChangePolicy(from.getFsGroupChangePolicy()); + } + if (podSecurityContext.getSeccompProfile() == null) { + podSecurityContext.seccompProfile(from.getSeccompProfile()); + } + if (podSecurityContext.getWindowsOptions() == null) { + podSecurityContext.windowsOptions(from.getWindowsOptions()); + } } } @@ -120,7 +134,7 @@ void fillInFrom(IntrospectorJobPod serverPod1) { envFrom.addAll(serverPod1.envFrom); } copyValues(resources, serverPod1.resources); - copyValues(podSecurityContext, serverPod1.podSecurityContext); + copyValues(serverPod1.podSecurityContext); } private void addIfMissing(V1EnvVar envVar) { diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java index 80b9bf2d1d7..174ecb0b5b0 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java @@ -202,10 +202,10 @@ class ServerPod extends KubernetesResource { * @since 2.0 */ @Description("Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. " - + "Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default " + + "If no value is specified for this field, the operator will use default " + "content for the pod-level `securityContext`. " + "More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.") - private V1PodSecurityContext podSecurityContext = new V1PodSecurityContext(); + private V1PodSecurityContext podSecurityContext = null; /** * InitContainers holds a list of initialization containers that should be run before starting the @@ -242,10 +242,10 @@ class ServerPod extends KubernetesResource { */ @Description("Container-level security attributes. Will override any matching Pod-level attributes. " + "See `kubectl explain pods.spec.containers.securityContext`. " - + "Beginning with operator version 4.0.5, if no value is specified for this field, the operator will use default " + + "If no value is specified for this field, the operator will use default " + "content for container-level `securityContext`. " + "More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.") - private V1SecurityContext containerSecurityContext = new V1SecurityContext(); + private V1SecurityContext containerSecurityContext = null; public List getVolumes() { return volumes; @@ -338,57 +338,85 @@ private static void copyValues(V1ResourceRequirements to, V1ResourceRequirements } @SuppressWarnings("Duplicates") - private void copyValues(V1PodSecurityContext to, V1PodSecurityContext from) { - if (to.getRunAsNonRoot() == null) { - to.runAsNonRoot(from.getRunAsNonRoot()); - } - if (to.getFsGroup() == null) { - to.fsGroup(from.getFsGroup()); - } - if (to.getRunAsGroup() == null) { - to.runAsGroup(from.getRunAsGroup()); - } - if (to.getRunAsUser() == null) { - to.runAsUser(from.getRunAsUser()); - } - if (to.getSeLinuxOptions() == null) { - to.seLinuxOptions(from.getSeLinuxOptions()); - } - if (to.getSupplementalGroups() == null) { - to.supplementalGroups(from.getSupplementalGroups()); - } - if (to.getSysctls() == null) { - to.sysctls(from.getSysctls()); + private void copyValues(V1PodSecurityContext from) { + if (from != null) { + if (podSecurityContext == null) { + podSecurityContext = new V1PodSecurityContext(); + } + if (podSecurityContext.getRunAsNonRoot() == null) { + podSecurityContext.runAsNonRoot(from.getRunAsNonRoot()); + } + if (podSecurityContext.getFsGroup() == null) { + podSecurityContext.fsGroup(from.getFsGroup()); + } + if (podSecurityContext.getRunAsGroup() == null) { + podSecurityContext.runAsGroup(from.getRunAsGroup()); + } + if (podSecurityContext.getRunAsUser() == null) { + podSecurityContext.runAsUser(from.getRunAsUser()); + } + if (podSecurityContext.getSeLinuxOptions() == null) { + podSecurityContext.seLinuxOptions(from.getSeLinuxOptions()); + } + if (podSecurityContext.getSupplementalGroups() == null) { + podSecurityContext.supplementalGroups(from.getSupplementalGroups()); + } + if (podSecurityContext.getSysctls() == null) { + podSecurityContext.sysctls(from.getSysctls()); + } + if (podSecurityContext.getFsGroupChangePolicy() == null) { + podSecurityContext.fsGroupChangePolicy(from.getFsGroupChangePolicy()); + } + if (podSecurityContext.getSeccompProfile() == null) { + podSecurityContext.seccompProfile(from.getSeccompProfile()); + } + if (podSecurityContext.getWindowsOptions() == null) { + podSecurityContext.windowsOptions(from.getWindowsOptions()); + } } } @SuppressWarnings("Duplicates") - private void copyValues(V1SecurityContext to, V1SecurityContext from) { - if (to.getAllowPrivilegeEscalation() == null) { - to.allowPrivilegeEscalation(from.getAllowPrivilegeEscalation()); - } - if (to.getPrivileged() == null) { - to.privileged(from.getPrivileged()); - } - if (to.getReadOnlyRootFilesystem() == null) { - to.readOnlyRootFilesystem(from.getReadOnlyRootFilesystem()); - } - if (to.getRunAsNonRoot() == null) { - to.runAsNonRoot(from.getRunAsNonRoot()); - } - if (to.getCapabilities() == null) { - to.setCapabilities(from.getCapabilities()); - } else { - copyValues(to.getCapabilities(), from.getCapabilities()); - } - if (to.getRunAsGroup() == null) { - to.runAsGroup(from.getRunAsGroup()); - } - if (to.getRunAsUser() == null) { - to.runAsUser(from.getRunAsUser()); - } - if (to.getSeLinuxOptions() == null) { - to.seLinuxOptions(from.getSeLinuxOptions()); + private void copyValues(V1SecurityContext from) { + if (from != null) { + if (containerSecurityContext == null) { + containerSecurityContext = new V1SecurityContext(); + } + if (containerSecurityContext.getAllowPrivilegeEscalation() == null) { + containerSecurityContext.allowPrivilegeEscalation(from.getAllowPrivilegeEscalation()); + } + if (containerSecurityContext.getPrivileged() == null) { + containerSecurityContext.privileged(from.getPrivileged()); + } + if (containerSecurityContext.getReadOnlyRootFilesystem() == null) { + containerSecurityContext.readOnlyRootFilesystem(from.getReadOnlyRootFilesystem()); + } + if (containerSecurityContext.getRunAsNonRoot() == null) { + containerSecurityContext.runAsNonRoot(from.getRunAsNonRoot()); + } + if (containerSecurityContext.getCapabilities() == null) { + containerSecurityContext.setCapabilities(from.getCapabilities()); + } else { + copyValues(containerSecurityContext.getCapabilities(), from.getCapabilities()); + } + if (containerSecurityContext.getRunAsGroup() == null) { + containerSecurityContext.runAsGroup(from.getRunAsGroup()); + } + if (containerSecurityContext.getRunAsUser() == null) { + containerSecurityContext.runAsUser(from.getRunAsUser()); + } + if (containerSecurityContext.getSeLinuxOptions() == null) { + containerSecurityContext.seLinuxOptions(from.getSeLinuxOptions()); + } + if (containerSecurityContext.getProcMount() == null) { + containerSecurityContext.procMount(from.getProcMount()); + } + if (containerSecurityContext.getSeccompProfile() == null) { + containerSecurityContext.seccompProfile(from.getSeccompProfile()); + } + if (containerSecurityContext.getWindowsOptions() == null) { + containerSecurityContext.windowsOptions(from.getWindowsOptions()); + } } } @@ -520,8 +548,8 @@ void fillInFrom(ServerPod serverPod1) { fillInFrom((KubernetesResource) serverPod1); serverPod1.nodeSelector.forEach(nodeSelector::putIfAbsent); copyValues(resources, serverPod1.resources); - copyValues(podSecurityContext, serverPod1.podSecurityContext); - copyValues(containerSecurityContext, serverPod1.containerSecurityContext); + copyValues(serverPod1.podSecurityContext); + copyValues(serverPod1.containerSecurityContext); if (maxReadyWaitTimeSeconds == null) { maxReadyWaitTimeSeconds = serverPod1.maxReadyWaitTimeSeconds; } diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/AdminPodHelperTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/AdminPodHelperTest.java index fd665a0c3ab..800fa9679e9 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/AdminPodHelperTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/AdminPodHelperTest.java @@ -127,31 +127,6 @@ void setServerPort(int port) { getServerTopology().setAdminPort(port); } - @Override - String getReferencePlainPortPodYaml_3_0() { - return ReferenceObjects.ADMIN_PLAINPORT_POD_3_0; - } - - @Override - String getReferencePlainPortPodYaml_3_1() { - return ReferenceObjects.ADMIN_PLAINPORT_POD_3_1; - } - - @Override - String getReferenceSslPortPodYaml_3_0() { - return ReferenceObjects.ADMIN_SSLPORT_POD_3_0; - } - - @Override - String getReferenceSslPortPodYaml_3_1() { - return ReferenceObjects.ADMIN_SSLPORT_POD_3_1; - } - - @Override - String getReferenceMiiPodYaml() { - return ReferenceObjects.ADMIN_MII_POD_3_1; - } - @Override String getReferenceMiiAuxImagePodYaml_3_3() { return ReferenceObjects.ADMIN_MII_AUX_IMAGE_POD_3_3; diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java index a53ef766d29..136022c42e6 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java @@ -113,6 +113,8 @@ import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createSecretKeyRefEnvVar; import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createSecurityContext; import static oracle.kubernetes.operator.helpers.PodHelperTestBase.createToleration; +import static oracle.kubernetes.operator.helpers.PodSecurityHelper.getDefaultContainerSecurityContext; +import static oracle.kubernetes.operator.helpers.PodSecurityHelper.getDefaultPodSecurityContext; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTBIT_CONFIGMAP_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTD_CONFIGMAP_NAME_SUFFIX; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTD_CONFIGMAP_VOLUME; @@ -1776,10 +1778,11 @@ void whenDomainHasContainerSecurityContext_introspectorPodContainersStartupWithI } @Test - void whenNotConfigured_introspectorPodContainers_hasEmptySecurityContext() { + void whenNotConfigured_introspectorPodContainers_hasDefaultSecurityContext() { V1JobSpec jobSpec = createJobSpec(); - getContainerStream(jobSpec).forEach(c -> assertThat(c.getSecurityContext(), is(new V1SecurityContext()))); + getContainerStream(jobSpec) + .forEach(c -> assertThat(c.getSecurityContext(), is(getDefaultContainerSecurityContext()))); } @Test @@ -1793,12 +1796,12 @@ void whenDomainHasPodSecurityContext_introspectorPodSpecStartupWithIt() { } @Test - void whenNotConfigured_introspectorPodSpec_hasEmptySecurityContext() { + void whenNotConfigured_introspectorPodSpec_hasDefaultSecurityContext() { V1JobSpec jobSpec = createJobSpec(); assertThat( getPodSpec(jobSpec).getSecurityContext(), - is(new V1PodSecurityContext())); + is(getDefaultPodSecurityContext())); } @Test diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/ManagedPodHelperTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/ManagedPodHelperTest.java index 98090bf0942..034b33d0ec2 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/ManagedPodHelperTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/ManagedPodHelperTest.java @@ -1310,31 +1310,6 @@ void setServerPort(int port) { getServerTopology().setListenPort(port); } - @Override - String getReferencePlainPortPodYaml_3_0() { - return ReferenceObjects.MANAGED_PLAINPORT_POD_3_0; - } - - @Override - String getReferencePlainPortPodYaml_3_1() { - return ReferenceObjects.MANAGED_PLAINPORT_POD_3_1; - } - - @Override - String getReferenceSslPortPodYaml_3_0() { - return ReferenceObjects.MANAGED_SSLPORT_POD_3_0; - } - - @Override - String getReferenceSslPortPodYaml_3_1() { - return ReferenceObjects.MANAGED_SSLPORT_POD_3_1; - } - - @Override - String getReferenceMiiPodYaml() { - return ReferenceObjects.MANAGED_MII_POD_3_1; - } - @Override String getReferenceMiiAuxImagePodYaml_3_3() { return ReferenceObjects.MANAGED_MII_AUX_IMAGE_POD_3_3; diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java index 46342396a6f..8f5b2b9ed26 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java @@ -795,21 +795,6 @@ void whenPodCreated_hasSha256HashAnnotationForRecipe() { assertThat(getCreatedPod().getMetadata().getAnnotations(), hasKey(SHA256_ANNOTATION)); } - // Returns the YAML for a 3.0 domain-in-image pod with only the plain port enabled. - abstract String getReferencePlainPortPodYaml_3_0(); - - // Returns the YAML for a 3.1 domain-in-image pod with only the plain port enabled. - abstract String getReferencePlainPortPodYaml_3_1(); - - // Returns the YAML for a 3.0 domain-in-image pod with the SSL port enabled. - abstract String getReferenceSslPortPodYaml_3_0(); - - // Returns the YAML for a 3.1 domain-in-image pod with the SSL port enabled. - abstract String getReferenceSslPortPodYaml_3_1(); - - // Returns the YAML for a 3.1 Mii Pod. - abstract String getReferenceMiiPodYaml(); - // Returns the YAML for a 3.3 Mii pod with aux image. abstract String getReferenceMiiAuxImagePodYaml_3_3(); @@ -824,18 +809,6 @@ void whenPodCreated_hasSha256HashAnnotationForRecipe() { abstract String getReferenceIstioMonitoringExporterTcpProtocol(); - @Test - void afterUpgradingPlainPortPodFrom30_patchIt() { - useProductionHash(); - initializeExistingPod(loadPodModel(getReferencePlainPortPodYaml_3_0())); - - verifyPodPatched(); - - V1Pod patchedPod = domainPresenceInfo.getServerPod(getServerName()); - assertThat(patchedPod.getMetadata().getLabels().get(OPERATOR_VERSION), equalTo(TEST_PRODUCT_VERSION)); - assertThat(AnnotationHelper.getHash(patchedPod), equalTo(AnnotationHelper.getHash(createPodModel()))); - } - @Test void afterUpgradingMiiDomainWith3_3_AuxImages_patchIt() { configureDomain().withInitContainer(createInitContainer()) @@ -922,63 +895,10 @@ void afterUpgradingMiiDomainWith4_0_AuxImages_patchIt() { assertThat(AnnotationHelper.getHash(patchedPod), equalTo(AnnotationHelper.getHash(createPodModel()))); } - @Test - void afterUpgradingPlainPortPodFrom31_patchIt() { - useProductionHash(); - initializeExistingPod(loadPodModel(getReferencePlainPortPodYaml_3_1())); - - verifyPodPatched(); - - V1Pod patchedPod = domainPresenceInfo.getServerPod(getServerName()); - assertThat(patchedPod.getMetadata().getLabels().get(OPERATOR_VERSION), equalTo(TEST_PRODUCT_VERSION)); - assertThat(AnnotationHelper.getHash(patchedPod), equalTo(AnnotationHelper.getHash(createPodModel()))); - } - - @Test - void afterUpgradingSslPortPodFrom30_patchIt() { - useProductionHash(); - getServerTopology().setSslListenPort(7002); - initializeExistingPod(loadPodModel(getReferenceSslPortPodYaml_3_0())); - - verifyPodPatched(); - - V1Pod patchedPod = domainPresenceInfo.getServerPod(getServerName()); - assertThat(patchedPod.getMetadata().getLabels().get(OPERATOR_VERSION), equalTo(TEST_PRODUCT_VERSION)); - assertThat(AnnotationHelper.getHash(patchedPod), equalTo(AnnotationHelper.getHash(createPodModel()))); - } - - @Test - void afterUpgradingSslPortPodFrom31_patchIt() { - useProductionHash(); - getServerTopology().setSslListenPort(7002); - initializeExistingPod(loadPodModel(getReferenceSslPortPodYaml_3_1())); - - verifyPodPatched(); - - V1Pod patchedPod = domainPresenceInfo.getServerPod(getServerName()); - assertThat(patchedPod.getMetadata().getLabels().get(OPERATOR_VERSION), equalTo(TEST_PRODUCT_VERSION)); - assertThat(AnnotationHelper.getHash(patchedPod), equalTo(AnnotationHelper.getHash(createPodModel()))); - } - void useProductionHash() { hashMemento.revert(); } - @Test - void afterUpgradingMiiPodFrom31_patchIt() { - useProductionHash(); - testSupport.addToPacket(SECRETS_MD_5, "originalSecret"); - testSupport.addToPacket(DOMAINZIP_HASH, "originalSecret"); - disableAutoIntrospectOnNewMiiPods(); - initializeExistingPod(loadPodModel(getReferenceMiiPodYaml())); - - verifyPodPatched(); - - V1Pod patchedPod = domainPresenceInfo.getServerPod(getServerName()); - assertThat(patchedPod.getMetadata().getLabels().get(OPERATOR_VERSION), equalTo(TEST_PRODUCT_VERSION)); - assertThat(AnnotationHelper.getHash(patchedPod), equalTo(AnnotationHelper.getHash(createPodModel()))); - } - private V1Pod loadPodModel(String podYaml) { return Yaml.loadAs(podYaml, V1Pod.class); } diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/ReferenceObjects.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/ReferenceObjects.java index a645df55a50..8c552e25de7 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/ReferenceObjects.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/ReferenceObjects.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Copyright (c) 2021, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -9,1212 +9,6 @@ * The settings here are taken from 3.0.0 and 3.1.0. */ class ReferenceObjects { - static final String MANAGED_PLAINPORT_POD_3_0 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '8001' - prometheus.io/scrape: 'true' - weblogic.sha256: '6e1c6a716a9e0078a56c2df86ffe1cd2f0f9bdbe271f169dc1d548d026aa52b7' - labels: - weblogic.domainName: domain1 - weblogic.serverName: ess_server1 - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-ess-server1 - namespace: namespace - ownerReferences: - - apiVersion: weblogic.oracle/v9 - kind: Domain - controller: true - name: domain1 - uid: '12345' - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: SERVER_NAME - value: ess_server1 - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-ess-server1 - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - - name: LOCAL_ADMIN_PORT - value: '8001' - - name: LOCAL_ADMIN_PROTOCOL - value: t3 - - name: SHUTDOWN_TYPE - value: Graceful - - name: SHUTDOWN_TIMEOUT - value: '30' - - name: SHUTDOWN_IGNORE_SESSIONS - value: 'false' - - name: DYNAMIC_CONFIG_OVERRIDE - value: 'true' - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 8001 - name: default - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 8001 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - terminationGracePeriodSeconds: 40 - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String MANAGED_PLAINPORT_POD_3_1 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '8001' - prometheus.io/scrape: 'true' - weblogic.sha256: 6e1c6a716a9e0078a56c2df86ffe1cd2f0f9bdbe271f169dc1d548d026aa52b7 - labels: - weblogic.domainName: domain1 - weblogic.serverName: ess_server1 - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-ess-server1 - namespace: namespace - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: SERVER_NAME - value: ess_server1 - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-ess-server1 - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 8001 - name: default - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 8001 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String ADMIN_PLAINPORT_POD_3_0 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '7001' - prometheus.io/scrape: 'true' - weblogic.sha256: 'fc735cf7c34bc1492a237149c4e27c9bb27575de463dbc55e953588862098df4' - labels: - weblogic.domainName: domain1 - weblogic.serverName: ADMIN_SERVER - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-admin-server - namespace: namespace - ownerReferences: - - apiVersion: weblogic.oracle/v9 - kind: Domain - controller: true - name: domain1 - uid: '12345' - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: SERVER_NAME - value: ADMIN_SERVER - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-admin-server - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - - name: LOCAL_ADMIN_PORT - value: '7001' - - name: LOCAL_ADMIN_PROTOCOL - value: t3 - - name: SHUTDOWN_TYPE - value: Graceful - - name: SHUTDOWN_TIMEOUT - value: '30' - - name: SHUTDOWN_IGNORE_SESSIONS - value: 'false' - - name: DYNAMIC_CONFIG_OVERRIDE - value: 'true' - - name: INTERNAL_OPERATOR_CERT - value: encoded-cert-data - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 7001 - name: default - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7001 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - hostname: uid1-admin-server - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - terminationGracePeriodSeconds: 40 - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - - static final String ADMIN_PLAINPORT_POD_3_1 - = """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '7001' - prometheus.io/scrape: 'true' - weblogic.sha256: fc735cf7c34bc1492a237149c4e27c9bb27575de463dbc55e953588862098df4 - labels: - weblogic.domainName: domain1 - weblogic.serverName: ADMIN_SERVER - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-admin-server - namespace: namespace - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: SERVER_NAME - value: ADMIN_SERVER - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-admin-server - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 7001 - name: default - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7001 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - hostname: uid1-admin-server - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String MANAGED_SSLPORT_POD_3_0 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '8001' - prometheus.io/scrape: 'true' - weblogic.sha256: '368b26eaf1645f966d4c350906b59a0afcc0fbdf9ed30779b71a6d044eb480dd' - labels: - weblogic.domainName: domain1 - weblogic.serverName: ess_server1 - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-ess-server1 - namespace: namespace - ownerReferences: - - apiVersion: weblogic.oracle/v9 - kind: Domain - controller: true - name: domain1 - uid: '12345' - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: ADMIN_PORT_SECURE - value: 'true' - - name: SERVER_NAME - value: ess_server1 - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-ess-server1 - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - - name: LOCAL_ADMIN_PORT - value: '7002' - - name: LOCAL_ADMIN_PROTOCOL - value: t3s - - name: SHUTDOWN_TYPE - value: Graceful - - name: SHUTDOWN_TIMEOUT - value: '30' - - name: SHUTDOWN_IGNORE_SESSIONS - value: 'false' - - name: DYNAMIC_CONFIG_OVERRIDE - value: 'true' - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 8001 - name: default - protocol: TCP - - containerPort: 7002 - name: default-secure - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7002 - scheme: HTTPS - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - terminationGracePeriodSeconds: 40 - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String MANAGED_SSLPORT_POD_3_1 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '7002' - prometheus.io/scrape: 'true' - weblogic.sha256: 368b26eaf1645f966d4c350906b59a0afcc0fbdf9ed30779b71a6d044eb480dd - labels: - weblogic.domainName: domain1 - weblogic.serverName: ess_server1 - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-ess-server1 - namespace: namespace - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: ADMIN_PORT_SECURE - value: 'true' - - name: SERVER_NAME - value: ess_server1 - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-ess-server1 - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 8001 - name: default - protocol: TCP - - containerPort: 7002 - name: default-secure - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7002 - scheme: HTTPS - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String ADMIN_SSLPORT_POD_3_0 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '7002' - prometheus.io/scrape: 'true' - weblogic.sha256: '25dad014916d4e459419f1a38c67f3f3e44d4f2256f0e9edeb89a6c86ca22428' - labels: - weblogic.domainName: domain1 - weblogic.serverName: ADMIN_SERVER - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-admin-server - namespace: namespace - ownerReferences: - - apiVersion: weblogic.oracle/v9 - kind: Domain - controller: true - name: domain1 - uid: '12345' - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7002' - - name: ADMIN_PORT_SECURE - value: 'true' - - name: ADMIN_SERVER_PORT_SECURE - value: 'true' - - name: SERVER_NAME - value: ADMIN_SERVER - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-admin-server - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - - name: LOCAL_ADMIN_PORT - value: '7002' - - name: LOCAL_ADMIN_PROTOCOL - value: t3s - - name: SHUTDOWN_TYPE - value: Graceful - - name: SHUTDOWN_TIMEOUT - value: '30' - - name: SHUTDOWN_IGNORE_SESSIONS - value: 'false' - - name: DYNAMIC_CONFIG_OVERRIDE - value: 'true' - - name: INTERNAL_OPERATOR_CERT - value: encoded-cert-data - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 7001 - name: default - protocol: TCP - - containerPort: 7002 - name: default-secure - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7002 - scheme: HTTPS - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - hostname: uid1-admin-server - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - terminationGracePeriodSeconds: 40 - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String ADMIN_SSLPORT_POD_3_1 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '7002' - prometheus.io/scrape: 'true' - weblogic.sha256: 854c2ce0b646268a7e72f601f625a8466f45d71f7f635a65ad3740ecb9bdf39a - labels: - weblogic.domainName: domain1 - weblogic.serverName: ADMIN_SERVER - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-admin-server - namespace: namespace - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7002' - - name: ADMIN_PORT_SECURE - value: 'true' - - name: ADMIN_SERVER_PORT_SECURE - value: 'true' - - name: SERVER_NAME - value: ADMIN_SERVER - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-admin-server - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 7001 - name: default - protocol: TCP - - containerPort: 7002 - name: default-secure - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7002 - scheme: HTTPS - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - hostname: uid1-admin-server - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume - """; - - static final String MANAGED_MII_POD_3_1 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '8001' - prometheus.io/scrape: 'true' - weblogic.sha256: bdebf4e8152fea393e06f037bbb4ed243a938f2f9e1aeeffe20768724a0b32cc - labels: - weblogic.domainName: domain1 - weblogic.serverName: ess_server1 - weblogic.modelInImageModelSecretsHash: md5.originalSecret.md5 - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.modelInImageDomainZipHash: md5.originalSecret.md5 - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-ess-server1 - namespace: namespace - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: SERVER_NAME - value: ess_server1 - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-ess-server1 - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 8001 - name: default - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 8001 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume"""; - - static final String ADMIN_MII_POD_3_1 = - """ - metadata: - annotations: - prometheus.io/path: /wls-exporter/metrics - prometheus.io/port: '7001' - prometheus.io/scrape: 'true' - weblogic.sha256: 98ac04bc5ddbc792ee37912ea5efe8097016ed10ce93b13f8e57013547429e6c - labels: - weblogic.domainName: domain1 - weblogic.serverName: ADMIN_SERVER - weblogic.modelInImageModelSecretsHash: md5.originalSecret.md5 - weblogic.domainRestartVersion: null - weblogic.domainUID: uid1 - weblogic.createdByOperator: 'true' - weblogic.modelInImageDomainZipHash: md5.originalSecret.md5 - weblogic.clusterRestartVersion: null - weblogic.serverRestartVersion: null - name: uid1-admin-server - namespace: namespace - spec: - containers: - - command: - - /weblogic-operator/scripts/startServer.sh - env: - - name: DOMAIN_NAME - value: domain1 - - name: DOMAIN_HOME - value: /u01/oracle/user_projects/domains - - name: ADMIN_NAME - value: ADMIN_SERVER - - name: ADMIN_PORT - value: '7001' - - name: SERVER_NAME - value: ADMIN_SERVER - - name: DOMAIN_UID - value: uid1 - - name: NODEMGR_HOME - value: /u01/nodemanager - - name: LOG_HOME - - name: SERVER_OUT_IN_POD_LOG - value: 'true' - - name: SERVICE_NAME - value: uid1-admin-server - - name: AS_SERVICE_NAME - value: uid1-admin-server - - name: USER_MEM_ARGS - value: -Djava.security.egd=file:/dev/./urandom - - name: ADMIN_USERNAME - - name: ADMIN_PASSWORD - image: image:latest - imagePullPolicy: Always - lifecycle: - preStop: - exec: - command: - - /weblogic-operator/scripts/stopServer.sh - livenessProbe: - exec: - command: - - /weblogic-operator/scripts/livenessProbe.sh - failureThreshold: 1 - initialDelaySeconds: 4 - periodSeconds: 6 - timeoutSeconds: 5 - name: weblogic-server - ports: - - containerPort: 7001 - name: default - protocol: TCP - readinessProbe: - failureThreshold: 1 - httpGet: - path: /weblogic/ready - port: 7001 - initialDelaySeconds: 1 - periodSeconds: 3 - timeoutSeconds: 2 - resources: - limits: {} - requests: {} - securityContext: {} - volumeMounts: - - mountPath: /weblogic-operator/scripts - name: weblogic-scripts-cm-volume - readOnly: true - - mountPath: /weblogic-operator/debug - name: weblogic-domain-debug-cm-volume - readOnly: true - - mountPath: /weblogic-operator/introspector - name: weblogic-domain-introspect-cm-volume - hostname: uid1-admin-server - imagePullSecrets: [] - initContainers: [] - nodeSelector: {} - securityContext: {} - volumes: - - configMap: - defaultMode: 365 - name: weblogic-scripts-cm - name: weblogic-scripts-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-debug-cm - optional: true - name: weblogic-domain-debug-cm-volume - - configMap: - defaultMode: 365 - name: uid1-weblogic-domain-introspect-cm - name: weblogic-domain-introspect-cm-volume"""; - static final String ADMIN_MII_AUX_IMAGE_POD_3_3 = """ metadata: From ef97dbfe43e554117c5186be0952a637b54838d9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 27 Sep 2024 14:33:27 -0400 Subject: [PATCH 174/356] Prepare for release 4.2.8 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index d6d9974e37f..ae22d2f0dbc 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 2080ff8796a..3fb669e0619 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index e05238bebed..9f04b846e54 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 486355cbd48..41461193880 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.8-SNAPSHOT + 4.2.8 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index d960507625b..48a1595a40f 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index ed665bc21cc..b93298cb955 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.8-SNAPSHOT + 4.2.8 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 79851240e60..2cca7040f3b 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 1877a0db285..53bf1a9014a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index c12b7f22689..ad407b51e84 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8-SNAPSHOT + 4.2.8 operator-swagger From 005f774d5f1ab2f964f029c75a24cd0f0a69e058 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 27 Sep 2024 15:16:27 -0400 Subject: [PATCH 175/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index ae22d2f0dbc..14adeea840f 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 3fb669e0619..4857f1f550d 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 9f04b846e54..4572817b671 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 41461193880..ac6a8320c80 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.8 + 4.2.9-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 48a1595a40f..5edde087cc4 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index b93298cb955..bb70c4a7b58 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.8 + 4.2.9-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 2cca7040f3b..6251a4d0007 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 53bf1a9014a..4091ec5f157 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index ad407b51e84..c2c13702735 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.8 + 4.2.9-SNAPSHOT operator-swagger From 83795c5ac6d404a6085c2f79158b0d1cc4f933c4 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 30 Sep 2024 17:31:47 -0400 Subject: [PATCH 176/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 4091ec5f157..7e7dbbc4ca3 100644 --- a/pom.xml +++ b/pom.xml @@ -681,14 +681,14 @@ 10.18.1 1.0 3.5.0 - 3.2.5 + 3.2.7 2.0.0.0 1.3.3 2.0.1 2.0.1 1.0.39 1.9.0 - 1.5.1 + 1.5.2 1.4.0 1.17.1 1.7.3 @@ -698,7 +698,7 @@ 3.6.0 1.0.0 3.26.3 - 2.16.1 + 2.17.0 4.2.2 19.0.1 3.0.1u2 @@ -706,7 +706,7 @@ 4.12.0 3.9.1 1.78.1 - 5.11.0 + 5.11.1 5.7.1 1.7.0 1.3.2 From 867a4f67cf555ffbe90139d569785d514a411add Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 1 Oct 2024 17:01:32 -0400 Subject: [PATCH 177/356] Document support for 1.30.1 --- .../site/content/introduction/prerequisites/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/introduction/prerequisites/introduction.md b/documentation/site/content/introduction/prerequisites/introduction.md index e6ba2229106..792b080e71d 100644 --- a/documentation/site/content/introduction/prerequisites/introduction.md +++ b/documentation/site/content/introduction/prerequisites/introduction.md @@ -7,7 +7,7 @@ weight: 5 For the current production release {{< latestVersion >}}: -* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, 1.28.2+, and 1.29.1+ (check with `kubectl version`). +* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, 1.28.2+, 1.29.1+, and 1.30.1+ (check with `kubectl version`). * Flannel networking v0.13.0-amd64 or later (check with `docker images | grep flannel`), Calico networking v3.16.1 or later, *or* OpenShift SDN on OpenShift 4.3 systems. * Docker 19.03.1+ (check with `docker version`) *or* CRI-O 1.20.2+ (check with `crictl version | grep RuntimeVersion`). From dc8fafd1ca46c6bb96439db3cfa06b6cbe797563 Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Wed, 2 Oct 2024 13:40:52 +0000 Subject: [PATCH 178/356] [wko-nightly] ItStickySession started failing since 8/26 on OKE r42 --- .../weblogic/kubernetes/ItStickySession.java | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java index 8ae4a1e3f01..d5b73f12b95 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItStickySession.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -53,6 +54,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.ImageUtils.createMiiImageAndVerify; @@ -182,7 +184,6 @@ void tearDown() { @DisplayName("Create a Traefik ingress resource and verify that two HTTP connections are sticky to the same server") @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") void testSameSessionStickinessUsingTraefik() { - final String channelName = "web"; // create Traefik ingress resource @@ -216,7 +217,11 @@ void testSameSessionStickinessUsingTraefik() { } // verify that two HTTP connections are sticky to the same server - sendHttpRequestsToTestSessionStickinessAndVerify(hostName, ingressServiceNodePort); + testUntil( + withLongRetryPolicy, + isHttpRequestsResponded(hostName, ingressServiceNodePort), + logger, + "Waiting until Http Requests response"); } /** @@ -230,23 +235,26 @@ void testSameSessionStickinessUsingTraefik() { @EnabledIfEnvironmentVariable(named = "OKD", matches = "true") void testSameSessionStickinessinOKD() { final String serviceName = domainUid + "-cluster-" + clusterName; - //final String channelName = "web"; // create route for cluster service String ingressHost = createRouteForOKD(serviceName, domainNamespace); // Since the app seems to take a bit longer to be available, // checking if the app is running by executing the curl command - String curlString - = buildCurlCommand(ingressHost, 0, SESSMIGR_APP_WAR_NAME + "/?getCounter", " -b "); + String curlString = buildCurlCommand(ingressHost, 0, SESSMIGR_APP_WAR_NAME + + "/?getCounter", " -b "); logger.info("Command to set HTTP request or get HTTP response {0} ", curlString); - testUntil( - assertDoesNotThrow(() - -> () -> exec(curlString, true).stdout().contains("managed-server")), + testUntil(assertDoesNotThrow(() + -> () -> exec(curlString, true).stdout().contains("managed-server")), logger, "Checking if app is available"); + // verify that two HTTP connections are sticky to the same server - sendHttpRequestsToTestSessionStickinessAndVerify(ingressHost, 0); + testUntil( + withLongRetryPolicy, + isHttpRequestsResponded(ingressHost, 0), + logger, + "Waiting until Http Requests response"); } /** @@ -278,7 +286,11 @@ void testSameSessionStickinessUsingClusterService() { logger.info("cluster port for cluster server {0} is: {1}", clusterAddress, clusterPort); // verify that two HTTP connections are sticky to the same server - sendHttpRequestsToTestSessionStickinessAndVerify(hostName, clusterPort, clusterAddress); + testUntil( + withLongRetryPolicy, + isHttpRequestsResponded(hostName, clusterPort, clusterAddress), + logger, + "Waiting until Http Requests response"); } private static String createAndVerifyDomainImage() { @@ -406,6 +418,10 @@ private Map getServerAndSessionInfoAndVerify(String hostName, String sessionId = httpAttrInfo.get(sessionIdAttr); String countStr = httpAttrInfo.get(countAttr); + if (serverName == null || sessionId == null || countStr == null) { + return new HashMap(); + } + // verify that the HTTP response data are not null assertAll("Check that WebLogic server and session vars is not null or empty", () -> assertNotNull(serverName,"Server name shouldn’t be null"), @@ -548,7 +564,15 @@ private int getIngressServiceNodePort(String nameSpace, String ingressServiceNam return ingressServiceNodePort; } - private void sendHttpRequestsToTestSessionStickinessAndVerify(String hostname, + private Callable isHttpRequestsResponded(String hostname, + int servicePort, + String... clusterAddress) { + return () -> { + return sendHttpRequestsToTestSessionStickinessAndVerify(hostname, servicePort, clusterAddress); + }; + } + + private boolean sendHttpRequestsToTestSessionStickinessAndVerify(String hostname, int servicePort, String... clusterAddress) { final int counterNum = 4; @@ -561,6 +585,7 @@ private void sendHttpRequestsToTestSessionStickinessAndVerify(String hostname, // send a HTTP request to set http session state(count number) and save HTTP session info Map httpDataInfo = getServerAndSessionInfoAndVerify(hostname, servicePort, webServiceSetUrl, " -c ", clusterAddress); + // get server and session info from web service deployed on the cluster String serverName1 = httpDataInfo.get(serverNameAttr); String sessionId1 = httpDataInfo.get(sessionIdAttr); @@ -570,6 +595,11 @@ private void sendHttpRequestsToTestSessionStickinessAndVerify(String hostname, // send a HTTP request again to get server and session info httpDataInfo = getServerAndSessionInfoAndVerify(hostname, servicePort, webServiceGetUrl, " -b ", clusterAddress); + + if (httpDataInfo.isEmpty()) { + return false; + } + // get server and session info from web service deployed on the cluster String serverName2 = httpDataInfo.get(serverNameAttr); String sessionId2 = httpDataInfo.get(sessionIdAttr); @@ -591,5 +621,7 @@ private void sendHttpRequestsToTestSessionStickinessAndVerify(String hostname, logger.info("SUCCESS --- test same session stickiness \n" + "Two HTTP connections are sticky to server {0} The session state " + "from the second HTTP connections is {2}", serverName2, SESSION_STATE); + + return true; } } From 80ba1177d9e4720baec0f1958a74f5ce905e85b8 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 2 Oct 2024 21:09:44 +0000 Subject: [PATCH 179/356] moved to weekly execution tests tagged with oke-weekly --- Jenkinsfile.oke | 18 +++++---- Jenkinsfile.podman | 4 +- Jenkinsfile.podman.upgrade | 2 +- integration-tests/pom.xml | 4 +- .../ItConfigDistributionStrategy.java | 2 +- .../kubernetes/ItCrossDomainTransaction.java | 2 +- .../ItCrossDomainTransactionSecurity.java | 2 +- .../weblogic/kubernetes/ItDBOperator.java | 2 +- .../weblogic/kubernetes/ItDedicatedMode.java | 2 +- .../kubernetes/ItFmwDomainInPVUsingWDT.java | 2 +- .../kubernetes/ItFmwDomainInPVUsingWLST.java | 2 +- .../ItFmwDomainInPvUserCreateRcu.java | 2 +- .../kubernetes/ItFmwDynamicDomainInPV.java | 2 +- ...tHorizontalPodAutoscalerCustomMetrics.java | 38 +++++++++++++++++-- .../kubernetes/ItLBTwoDomainsNginx.java | 2 +- .../kubernetes/ItLBTwoDomainsTraefik.java | 2 +- .../ItLiftAndShiftFromOnPremDomain.java | 2 +- .../ItLivenessProbeCustomization.java | 2 +- .../weblogic/kubernetes/ItMiiDomain.java | 2 +- .../kubernetes/ItMiiUpdateDomainConfig.java | 2 +- .../ItMonitoringExporterMetricsFiltering.java | 2 +- .../ItMonitoringExporterSamples.java | 2 +- .../ItMonitoringExporterSideCar.java | 2 +- .../ItMonitoringExporterWebApp.java | 2 +- .../kubernetes/ItOperatorRestart.java | 2 +- .../kubernetes/ItPodsShutdownOption.java | 2 +- .../weblogic/kubernetes/ItT3Channel.java | 2 +- .../ItTwoDomainsManagedByTwoOperators.java | 2 +- .../oracle/weblogic/kubernetes/ItWseeSSO.java | 19 +++++++++- .../kubernetes/utils/CommonLBTestUtils.java | 34 +++++++++++++++++ 30 files changed, 125 insertions(+), 40 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 2d9ecdb73ac..76a50f8e2bc 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -3,11 +3,11 @@ // -CRON_SETTINGS_MAIN = '''H 3 * * 1-5 % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seqone;PARALLEL_RUN=false - H 2 * * 1-5 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' -CRON_SETTINGS_42 = '''H 1 * * 1-5 % MAVEN_PROFILE_NAME=oke-gate;CLUSTER_NAME=seq42one;PARALLEL_RUN=false - H 11 * * 1-5 % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false - H 14 * * 1-5 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' +CRON_SETTINGS_MAIN = '''H 3 * * 0-4 % MAVEN_PROFILE_NAME=oke-weekly-sequential;CLUSTER_NAME=seqweekly;PARALLEL_RUN=false + H 2 * * 0-4 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' +CRON_SETTINGS_42 = '''H 1 * * 5 % MAVEN_PROFILE_NAME=oke-weekly-sequential;CLUSTER_NAME=seq42weekly;PARALLEL_RUN=false + H 23 * * 0-4 % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false + H 18 * * 0-4 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' @@ -117,9 +117,9 @@ pipeline { ) choice(name: 'MAVEN_PROFILE_NAME', - description: 'Profile to use in mvn command to run the tests. Possible values are oke-gate,oke-parallel, oke-sequential. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + description: 'Profile to use in mvn command to run the tests. Possible values are oke-weekly-sequential,oke-parallel, oke-sequential. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', choices: [ - 'oke-gate', + 'oke-weekly-sequential', 'oke-sequential', 'oke-parallel' ] @@ -633,6 +633,8 @@ EOF export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" + export no_proxy="${NO_PROXY},localhost,127.0.0.1,.us.oracle.com,.oraclecorp.com,login.oracle.com" + export NO_PROXY="${no_proxy}" if ! mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/oketest.log"; then echo "integration-tests failed" @@ -677,7 +679,7 @@ EOF ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform ${AVAILABILITY_DOMAIN} fi - if [ "${MAVEN_PROFILE_NAME}" = "oke-gate" ] && [ "${BRANCH}" = "release/4.2" ]; then + if [ "${MAVEN_PROFILE_NAME}" = "oke-sequential" ] && [ "${BRANCH}" = "release/4.2" ]; then compname="wkt" wkt_compartment_ocid=$(oci iam compartment list --compartment-id-in-subtree true --all | jq --arg compname "$compname" '.data[] | select(."name"==$compname)' | jq -r ."id") sec_list_id=$(oci network security-list list --compartment-id="$wkt_compartment_ocid" --display-name=Security-List-wktiso1 | jq -r '.data[] | ."id"') diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index fcf3f71d74d..2e661a3ca39 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -18,8 +18,8 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 1 * * 1-5 % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * 1-5 % MAVEN_PROFILE_NAME=kind-sequential''' +CRON_SETTINGS = '''H 1 * * 0-4 % MAVEN_PROFILE_NAME=kind-parallel + H 2 * * 0-4 % MAVEN_PROFILE_NAME=kind-sequential''' pipeline { agent { label 'large-ol9u4' } diff --git a/Jenkinsfile.podman.upgrade b/Jenkinsfile.podman.upgrade index 308a8c9cf7c..e03f72987e5 100644 --- a/Jenkinsfile.podman.upgrade +++ b/Jenkinsfile.podman.upgrade @@ -20,7 +20,7 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 4 * * * % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.17''' +CRON_SETTINGS = '''H 4 * * 0-4 % MAVEN_PROFILE_NAME=kind-upgrade;KUBE_VERSION=1.24.17''' pipeline { agent { label 'large-ol9u4' } diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 4572817b671..0b5c8941eb7 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -469,10 +469,10 @@ - oke-gate + oke-weekly-sequential false - oke-gate + oke-weekly-sequential diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index 4ab3709ecbb..898baee1e1c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -136,7 +136,7 @@ @DisplayName("Verify the overrideDistributionStrategy applies the overrides accordingly to the value set") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @IntegrationTest @Tag("olcne-mrg") class ItConfigDistributionStrategy { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index 94f460c2ea7..e689451ad3c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -699,7 +699,7 @@ private static void createNginxIngressPathRoutingRules() { //restart core-dns service result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); logger.info(result.stdout()); - checkPodReady("core-dns", null, "kube-system"); + checkPodReady("coredns", null, "kube-system"); result = ExecCommand.exec(curlCmd); logger.info(result.stdout()); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java index c66db521ec6..79e41c0319d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java @@ -99,7 +99,7 @@ @DisplayName("Verify cross domain transaction is successful with CrossDomainSecurityEnabled set to true") @IntegrationTest @Tag("kind-parallel") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") class ItCrossDomainTransactionSecurity { private static final String auxImageName1 = DOMAIN_IMAGES_PREFIX + "domain1-cdxaction-aux"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java index 26d04cea7e3..995fce0a56a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDBOperator.java @@ -109,7 +109,7 @@ @DisplayName("Test to create FMW model in image domain and WebLogic domain using Oracle " + "database created using Oracle Database Operator") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("kind-parallel") class ItDBOperator { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java index f3326916693..9228260172f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDedicatedMode.java @@ -68,7 +68,7 @@ */ @DisplayName("Test Operator and WebLogic domain with Dedicated set to true") @Tag("kind-sequential") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("okd-wls-mrg") @IntegrationTest class ItDedicatedMode { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java index 0a4eb3fe93f..de6333b962a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWDT.java @@ -62,7 +62,7 @@ */ @DisplayName("Test to creat a FMW domain in persistent volume using WDT") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("kind-sequential") @Tag("okd-fmw-cert") class ItFmwDomainInPVUsingWDT { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index 52d7b1bf74e..ea0c8654e7e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -74,7 +74,7 @@ @Tag("okd-fmw-cert") @IntegrationTest @Tag("olcne-sequential") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") class ItFmwDomainInPVUsingWLST { private static String dbNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java index 544e7ac413e..9e92cdb35c4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java @@ -99,7 +99,7 @@ @DisplayName("Test for initializeDomainOnPV when user per-creates RCU") @IntegrationTest @Tag("kind-sequential") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") public class ItFmwDomainInPvUserCreateRcu { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java index f0cb972f631..aac0d4207b1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDynamicDomainInPV.java @@ -78,7 +78,7 @@ */ @DisplayName("Test to creat a FMW dynamic domain in persistent volume using WLST") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("kind-sequential") @Tag("okd-fmw-cert") class ItFmwDynamicDomainInPV { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java index 4aaec28c08f..a370aa4c5d3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java @@ -100,6 +100,7 @@ import static oracle.weblogic.kubernetes.utils.MonitoringUtils.installMonitoringExporter; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPvAndPvc; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.isPodDeleted; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -112,7 +113,7 @@ @DisplayName("Test to a create MII domain and test autoscaling using HPA and" + "custom metrics provided via use of monitoring exporter and prometheus and prometheus adapter") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("kind-parallel") public class ItHorizontalPodAutoscalerCustomMetrics { private static final String MONEXP_MODEL_FILE = "model.monexp.custommetrics.yaml"; @@ -318,15 +319,46 @@ void testHPAWithCustomMetrics() { hostPort = host + ":" + nodeportshttp; } String curlCmd = - String.format("curl --silent --show-error --noproxy '*' -H 'host: %s' http://%s:%s@%s/" + SESSMIGT_APP_URL, + String.format("curl -g --silent --show-error -v --noproxy '*' -H 'host: %s' http://%s:%s@%s/" + SESSMIGT_APP_URL, ingressHostList.get(0), ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, hostPort); logger.info("Executing curl command " + curlCmd); + assertDoesNotThrow(() -> { + ExecResult result = ExecCommand.exec(curlCmd, true); + String response = result.stdout().trim(); + getLogger().info("exitCode: {0}, \nstdout: {1}, \nstderr: {2}", + result.exitValue(), response, result.stderr()); + if (!response.contains("cluster-1-managed")) { + logger.info("Can't invoke application"); + + if (OKE_CLUSTER) { + LoggingFacade logger = getLogger(); + try { + + result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); + logger.info(result.stdout()); + //restart core-dns service + result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); + logger.info(result.stdout()); + checkPodReady("coredns", null, "kube-system"); + + } catch (Exception ex) { + logger.warning(ex.getLocalizedMessage()); + } + } + } + }); for (int i = 0; i < 50; i++) { - assertDoesNotThrow(() -> ExecCommand.exec(curlCmd)); + assertDoesNotThrow(() -> { + ExecResult result = ExecCommand.exec(curlCmd, true); + String response = result.stdout().trim(); + getLogger().info("exitCode: {0}, \nstdout: {1}, \nstderr: {2}", + result.exitValue(), response, result.stderr()); + assertTrue(response.contains("cluster-1-managed"), "Can't invoke application"); + }); } //check hpa scaled up to one more server checkPodReadyAndServiceExists(managedServerPrefix + 3, domainUid, domainNamespace); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index afc865d35df..1d174860f0e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -76,7 +76,7 @@ @IntegrationTest @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("oke-gate") +@Tag("oke-sequential") class ItLBTwoDomainsNginx { private static final int numberOfDomains = 2; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java index b47bf1916dd..de3a08afd16 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java @@ -62,7 +62,7 @@ @IntegrationTest @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("oke-gate") +@Tag("oke-sequential") class ItLBTwoDomainsTraefik { private static final int numberOfDomains = 2; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java index 08b410ed9ee..c505cb88eb2 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java @@ -105,7 +105,7 @@ @Tag("toolkits-srg") @Tag("okd-wls-mrg") @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @IntegrationTest class ItLiftAndShiftFromOnPremDomain { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java index ba3877ef289..5b06cad8f46 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLivenessProbeCustomization.java @@ -86,7 +86,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") class ItLivenessProbeCustomization { // domain constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java index 88e218ad06d..5115c1aa428 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomain.java @@ -137,7 +137,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-srg") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") class ItMiiDomain { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index 1d12ea40fee..a65dc22844a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -119,7 +119,7 @@ @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-srg") -@Tag("oke-sequential") +@Tag("oke-weekly-sequential") class ItMiiUpdateDomainConfig { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java index 4c21fb21ced..50540592af7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java @@ -90,7 +90,7 @@ @DisplayName("Verify WebLogic Metric is processed and filtered as expected by MonitoringExporter") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("kind-sequential") @Tag("okd-wls-mrg") class ItMonitoringExporterMetricsFiltering { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java index 7bed8798952..41d0ee554b6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java @@ -135,7 +135,7 @@ * Verify WebLogic metrics can be accessed via Prometheus */ @DisplayName("Verify end to end sample, provided in the Monitoring Exporter github project") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("kind-parallel") @Tag("okd-wls-mrg") @IntegrationTest diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java index 64bb8efeed0..f3a6b2a4c1f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java @@ -101,7 +101,7 @@ + "MonitoringExporter Side Car via Prometheus and Grafana") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("kind-parallel") @Tag("okd-wls-mrg") class ItMonitoringExporterSideCar { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java index 076de002905..16b17dfada7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java @@ -107,7 +107,7 @@ @DisplayName("Verify WebLogic Metric is processed as expected by MonitoringExporter WebApp via Prometheus and Grafana") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("kind-sequential") @Tag("okd-wls-mrg") class ItMonitoringExporterWebApp { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorRestart.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorRestart.java index d28e73090cc..ca37399e0fb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorRestart.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorRestart.java @@ -59,7 +59,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("okd-wls-mrg") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("oke-arm") class ItOperatorRestart { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java index 733ab1025c8..62985abfc66 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java @@ -82,7 +82,7 @@ @Tag("kind-parallel") @Tag("okd-wls-mrg") @Tag("oke-arm") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") class ItPodsShutdownOption { private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index e1ee2b920aa..9fbef0ff057 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -97,7 +97,7 @@ @DisplayName("Test T3 channel deployment") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("kind-sequential") class ItT3Channel { // namespace constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItTwoDomainsManagedByTwoOperators.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItTwoDomainsManagedByTwoOperators.java index 2c8788fd12a..521ed38245c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItTwoDomainsManagedByTwoOperators.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItTwoDomainsManagedByTwoOperators.java @@ -108,7 +108,7 @@ @IntegrationTest @Tag("olcne-srg") @Tag("kind-parallel") -@Tag("oke-gate") +@Tag("oke-weekly-sequential") @Tag("oke-arm") class ItTwoDomainsManagedByTwoOperators { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java index 5865ba17f5b..d515f6cdecd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWseeSSO.java @@ -28,6 +28,7 @@ import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; import oracle.weblogic.kubernetes.utils.OracleHttpClient; import org.junit.jupiter.api.BeforeAll; @@ -47,6 +48,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IT_WSEESSONGINX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.NGINX_CHART_VERSION; @@ -90,6 +92,7 @@ import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVC; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.execInPod; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; @@ -107,7 +110,7 @@ */ @DisplayName("Verify that client can communicate with webservices with SSO") @IntegrationTest -@Tag("oke-gate") +@Tag("oke-sequential") @Tag("kind-parallel") class ItWseeSSO { @@ -266,6 +269,20 @@ private String checkWSDLAccess(String domainNamespace, String domainUid, hostAndPort = ingressIP + ":80"; } String url = "http://" + hostAndPort + appURI; + if (OKE_CLUSTER) { + try { + if (OracleHttpClient.get(url, true).statusCode() != 200) { + ExecResult result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); + logger.info(result.stdout()); + //restart core-dns service + result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); + logger.info(result.stdout()); + checkPodReady("coredns", null, "kube-system"); + } + } catch (Exception ex) { + logger.warning(ex.getLocalizedMessage()); + } + } assertEquals(200, OracleHttpClient.get(url, true).statusCode()); return url; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java index 2ed9aa710ec..68f0f177f01 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java @@ -64,6 +64,7 @@ import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PV_ROOT; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; @@ -95,6 +96,7 @@ import static oracle.weblogic.kubernetes.utils.JobUtils.createJobAndWaitUntilComplete; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVPVCAndVerify; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createfixPVCOwnerContainer; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; @@ -882,6 +884,38 @@ public static void verifyAdminServerAccess(boolean isTLS, getLogger().severe(ex.getMessage()); } } + if (OKE_CLUSTER) { + LoggingFacade logger = getLogger(); + try { + if (!consoleAccessible) { + ExecResult result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); + logger.info(result.stdout()); + //restart core-dns service + result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); + logger.info(result.stdout()); + checkPodReady("coredns", null, "kube-system"); + } + } catch (Exception ex) { + logger.warning(ex.getLocalizedMessage()); + } + for (int i = 0; i < 10; i++) { + assertDoesNotThrow(() -> TimeUnit.SECONDS.sleep(1)); + ExecResult result; + try { + getLogger().info("Accessing app on admin server using curl request, iteration {0}: {1}", i, curlCmd); + result = ExecCommand.exec(curlCmd, true); + String response = result.stdout().trim(); + getLogger().info("exitCode: {0}, \nstdout: {1}, \nstderr: {2}", + result.exitValue(), response, result.stderr()); + if (response.contains("RUNNING")) { + consoleAccessible = true; + break; + } + } catch (IOException | InterruptedException ex) { + getLogger().severe(ex.getMessage()); + } + } + } assertTrue(consoleAccessible, "Couldn't access admin server app"); } } From d86fb52bf4f7f8263ec533b02bf2e99f8b0d8357 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Thu, 3 Oct 2024 23:13:36 +0000 Subject: [PATCH 180/356] Add missing secrets --- .../oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java index 78abfcfa75e..86cb7a83739 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java @@ -394,7 +394,8 @@ private void launchPvHelperPod(String namespace, String pvcName) { String podName = "pvhelper"; String script = ITTESTS_DIR + "/../kubernetes/samples/scripts/domain-lifecycle/pv-pvc-helper.sh"; - String command = "/bin/bash " + script + " -n " + namespace + " -c " + pvcName + " -m /shared -i " + image1412; + String command = "/bin/bash " + script + " -n " + namespace + " -p " + BASE_IMAGES_REPO_SECRET_NAME + + " -c " + pvcName + " -m /shared -i " + image1412; ExecResult result = null; try { result = exec(new String(command), true); From 176196b172cb1e5e655ef23d152b5c28eb7fe8a5 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 4 Oct 2024 17:30:59 +0000 Subject: [PATCH 181/356] Enhance existing tests to make right after failed domain status --- .../ItDiagnosticsFailedCondition.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java index 0497c2f9b60..8b26194bc9d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java @@ -54,6 +54,8 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_COMPLETED_TYPE; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_FAILED_TYPE; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; @@ -61,6 +63,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.deleteClusterCustomResource; @@ -78,6 +81,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.configMapExist; +import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapFromFiles; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; import static oracle.weblogic.kubernetes.utils.DbUtils.setupDBandRCUschema; @@ -89,8 +93,10 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createImageRegistrySecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createMiiImageAndVerify; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResource; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createOpsswalletpasswordSecret; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; @@ -853,6 +859,111 @@ rcuSchemaPrefix, domainNamespace, getNextFreePort(), dbUrl, dbListenerPort), } } + /** + * Test domain status condition with a bad model file. + * Verify the following conditions are generated in an order after an introspector failure. + * type: Failed, status: true + * type: Available, status: false + * type: Completed, status: false + * Verify the introspector reruns to make it right when model file is fixed. + */ + @Test + @DisplayName("Test domain status condition with bad model file") + void testIntrospectorMakerightAvailableFromFailure() { + // Create the repo secret to pull the image + // this secret is used only for non-kind cluster + createTestRepoSecret(domainNamespace); + + // create secret for admin credentials + logger.info("Create secret for admin credentials"); + assertDoesNotThrow(() -> createSecretWithUsernamePassword( + adminSecretName, + domainNamespace, + ADMIN_USERNAME_DEFAULT, + ADMIN_PASSWORD_DEFAULT), + String.format("createSecret failed for %s", adminSecretName)); + + // create encryption secret + logger.info("Create encryption secret"); + assertDoesNotThrow(() -> createSecretWithUsernamePassword( + encryptionSecretName, + domainNamespace, + ENCRYPION_USERNAME_DEFAULT, + ENCRYPION_PASSWORD_DEFAULT), + String.format("createSecret failed for %s", encryptionSecretName)); + + // create WDT config map without any files + createConfigMapAndVerify("empty-cm", domainUid, domainNamespace, Collections.emptyList()); + String image = MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG; + + // create the domain object + DomainResource domain = createDomainResourceWithConfigMap(domainUid, + domainNamespace, + adminSecretName, + TEST_IMAGES_REPO_SECRET_NAME, + encryptionSecretName, + 2, + image, + "empty-cm", + 180L, + "mymii-cluster-resource"); + + logger.info("Creating a domain resource with model file image"); + createDomainAndVerify(domain, domainNamespace); + checkDomainStatusConditionTypeHasExpectedStatus(domainUid, domainNamespace, + DOMAIN_STATUS_CONDITION_COMPLETED_TYPE, "True"); + + //patch the domain with bad image + //check the desired completed, available and failed status + //verify the condition type Failed exists + StringBuffer patchStr = new StringBuffer("[{"); + patchStr.append("\"op\": \"replace\",") + .append(" \"path\": \"/spec/image\",") + .append("\"value\": \"") + .append("bad-mii-image:doesntexist") + .append("\"}]"); + logger.info("PatchStr for imageUpdate: {0}", patchStr.toString()); + + assertTrue(patchDomainResource(domainUid, domainNamespace, patchStr), + "patchDomainCustomResource(imageUpdate) failed"); + checkDomainStatusConditionTypeHasExpectedStatus(domainUid, domainNamespace, + DOMAIN_STATUS_CONDITION_FAILED_TYPE, "True"); + + //fix the domain failure by patching the domain resource with good image + patchStr = new StringBuffer("[" + + "{"); + patchStr.append("\"op\": \"replace\",") + .append(" \"path\": \"/spec/image\",") + .append("\"value\": \"") + .append(MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG) + .append("\"},") + .append("{\"op\": \"add\",") + .append(" \"path\": \"/spec/restartVersion\",").append("\"value\": ").append("\"1\"") + .append("}" + + "]"); + logger.info("PatchStr for imageUpdate: {0}", patchStr.toString()); + + assertTrue(patchDomainResource(domainUid, domainNamespace, patchStr), + "patchDomainCustomResource(imageUpdate) failed"); + + final String adminServerPodName = domainUid + "-admin-server"; + final String managedServerPrefix = domainUid + "-managed-server"; + + // check admin server pod is ready + logger.info("Wait for admin server pod {0} to be ready in namespace {1}", + adminServerPodName, domainNamespace); + checkPodReadyAndServiceExists(adminServerPodName, domainUid, domainNamespace); + // check managed server pods are ready + for (int i = 1; i <= 2; i++) { + logger.info("Wait for managed server pod {0} to be ready in namespace {1}", + managedServerPrefix + i, domainNamespace); + checkPodReadyAndServiceExists(managedServerPrefix + i, domainUid, domainNamespace); + } + checkDomainStatusConditionTypeHasExpectedStatus(domainUid, domainNamespace, + DOMAIN_STATUS_CONDITION_COMPLETED_TYPE, "True"); + checkDomainStatusConditionTypeExists(domainUid, domainNamespace, DOMAIN_STATUS_CONDITION_AVAILABLE_TYPE); + } + // Create a domain resource with a custom ConfigMap private DomainResource createDomainResourceWithConfigMap(String domainUid, String domNamespace, String adminSecretName, From a921de5b31070b09299dddbfcbaabc9d1d0c3662 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Fri, 4 Oct 2024 21:19:34 +0000 Subject: [PATCH 182/356] Use correct test class name when creating domain configuration --- .../ItConfigDistributionStrategy.java | 5 +-- .../kubernetes/ItIntrospectVersion.java | 23 +------------ .../kubernetes/ItKubernetesDomainEvents.java | 5 +-- .../kubernetes/ItSystemResOverrides.java | 5 +-- .../weblogic/kubernetes/ItT3Channel.java | 5 +-- .../weblogic/kubernetes/utils/FileUtils.java | 33 +++++++++++++++++++ 6 files changed, 46 insertions(+), 30 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index 898baee1e1c..71424109701 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -104,6 +104,7 @@ import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; +import static oracle.weblogic.kubernetes.utils.FileUtils.createWdtPropertyFile; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; @@ -983,7 +984,7 @@ private void createDomain() { final String wlsModelFilePrefix = "model-dci-introspect"; final String wlsModelFile = wlsModelFilePrefix + ".yaml"; t3ChannelPort = getNextFreePort(); - File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; @@ -1013,7 +1014,7 @@ private void createDomain() { configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); } else { configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, - ItIntrospectVersion.class.getName()); + ItConfigDistributionStrategy.class.getSimpleName()); } configuration.getInitializeDomainOnPV().domain(new DomainOnPV() .createMode(CreateIfNotExists.DOMAIN) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index 05ee4cea5da..b847ce00e93 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -135,6 +135,7 @@ import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; +import static oracle.weblogic.kubernetes.utils.FileUtils.createWdtPropertyFile; import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createMiiImageAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; @@ -1506,26 +1507,4 @@ private void updateIngressBackendServicePort(int newAdminPort) throws ApiExcepti fail("Ingress is null, failed to update ingress"); } } - - public static File createWdtPropertyFile(String wlsModelFilePrefix, String nodePortHost, int t3Port) { - - // create property file used with domain model file - Properties p = new Properties(); - p.setProperty("WebLogicAdminUserName", ADMIN_USERNAME_DEFAULT); - p.setProperty("WebLogicAdminPassword", ADMIN_PASSWORD_DEFAULT); - p.setProperty("K8S_NODEPORT_HOST", nodePortHost); - p.setProperty("T3_CHANNEL_PORT", Integer.toString(t3Port)); - - // create a model property file - File domainPropertiesFile = assertDoesNotThrow(() -> - File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), - "Failed to create WLS model properties file"); - - // create the property file - assertDoesNotThrow(() -> - p.store(new FileOutputStream(domainPropertiesFile), "WLS properties file"), - "Failed to write WLS properties file"); - - return domainPropertiesFile; - } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index bf2526beae0..b82aabea4e1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -93,6 +93,7 @@ import static oracle.weblogic.kubernetes.utils.DomainUtils.deleteDomainResource; import static oracle.weblogic.kubernetes.utils.DomainUtils.removeClusterInDomainResource; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; +import static oracle.weblogic.kubernetes.utils.FileUtils.createWdtPropertyFile; import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; @@ -771,7 +772,7 @@ private static DomainResource createDomain(String domainNamespace, String domain final String wlsModelFilePrefix = "model-dci-introspect"; final String wlsModelFile = wlsModelFilePrefix + ".yaml"; int t3ChannelPort = getNextFreePort(); - File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage @@ -802,7 +803,7 @@ private static DomainResource createDomain(String domainNamespace, String domain configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); } else { configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, - ItIntrospectVersion.class.getName()); + ItKubernetesDomainEvents.class.getSimpleName()); } configuration.getInitializeDomainOnPV().domain(new DomainOnPV() .createMode(CreateIfNotExists.DOMAIN) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index 5f5cfc86b83..814603e648b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -90,6 +90,7 @@ import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapFromFiles; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.FileUtils.createWdtPropertyFile; import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; @@ -393,7 +394,7 @@ private void createDomain() { final String wlsModelFilePrefix = "sitconfig-dci-model"; final String wlsModelFile = wlsModelFilePrefix + ".yaml"; t3ChannelPort = getNextFreePort(); - File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage @@ -424,7 +425,7 @@ private void createDomain() { configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); } else { configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, - ItIntrospectVersion.class.getName()); + ItSystemResOverrides.class.getSimpleName()); } configuration.getInitializeDomainOnPV().domain(new DomainOnPV() .createMode(CreateIfNotExists.DOMAIN) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 9fbef0ff057..225c410e7e8 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -80,6 +80,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.FileUtils.createWdtPropertyFile; import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; @@ -174,7 +175,7 @@ void testAdminServerT3Channel() { final String wlsModelFilePrefix = "model-dci-introspect"; final String wlsModelFile = wlsModelFilePrefix + ".yaml"; - File wlsModelPropFile = ItIntrospectVersion.createWdtPropertyFile(wlsModelFilePrefix, + File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); // create domainCreationImage @@ -205,7 +206,7 @@ void testAdminServerT3Channel() { configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); } else { configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, - ItIntrospectVersion.class.getName()); + ItT3Channel.class.getSimpleName()); } configuration.getInitializeDomainOnPV().domain(new DomainOnPV() .createMode(CreateIfNotExists.DOMAIN) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java index 8296edfc63c..d5b85cb1d47 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java @@ -17,6 +17,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.concurrent.Callable; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,8 +32,11 @@ import oracle.weblogic.kubernetes.logging.LoggingFacade; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.actions.ActionConstants.DOWNLOAD_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_FILENAME_DEFAULT; @@ -616,4 +620,33 @@ public static boolean searchStringInFile(String fileName, String searchString) t logger.info("Failed to find string {0} in the file {1}", searchString, fileName); return false; } + + /** + * Create WDT property file. + * @param wlsModelFilePrefix the model file prefix + * @param nodePortHost the K8S node port host name + * @param t3Port T3_CHANNEL_PORT + * @return WDT property file + */ + public static File createWdtPropertyFile(String wlsModelFilePrefix, String nodePortHost, int t3Port) { + + // create property file used with domain model file + Properties p = new Properties(); + p.setProperty("WebLogicAdminUserName", ADMIN_USERNAME_DEFAULT); + p.setProperty("WebLogicAdminPassword", ADMIN_PASSWORD_DEFAULT); + p.setProperty("K8S_NODEPORT_HOST", nodePortHost); + p.setProperty("T3_CHANNEL_PORT", Integer.toString(t3Port)); + + // create a model property file + File domainPropertiesFile = assertDoesNotThrow(() -> + File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), + "Failed to create WLS model properties file"); + + // create the property file + assertDoesNotThrow(() -> + p.store(new FileOutputStream(domainPropertiesFile), "WLS properties file"), + "Failed to write WLS properties file"); + + return domainPropertiesFile; + } } From 7f9b23806847f1d58993064c334369832a52f2bc Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 4 Oct 2024 17:59:13 -0400 Subject: [PATCH 183/356] Dependency updates --- pom.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pom.xml b/pom.xml index 7e7dbbc4ca3..635c40e0422 100644 --- a/pom.xml +++ b/pom.xml @@ -640,6 +640,21 @@ jose4j ${jose4j-version} + + com.google.protobuf + protobuf-java + ${protobuf-java-version} + + + net.minidev + json-smart + ${json-smart-version} + + + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt-version} + @@ -723,6 +738,9 @@ 10.0.4 2.0.16 1.5.8 + 3.25.5 + 2.4.11 + 9.41.2 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From dc5121b1f371f23453bcd9ad90a9e77cd5e53947 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Fri, 4 Oct 2024 23:40:22 +0000 Subject: [PATCH 184/356] switched to Saturday execution oke weekly --- Jenkinsfile.oke | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 76a50f8e2bc..3975457aa58 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -5,7 +5,7 @@ CRON_SETTINGS_MAIN = '''H 3 * * 0-4 % MAVEN_PROFILE_NAME=oke-weekly-sequential;CLUSTER_NAME=seqweekly;PARALLEL_RUN=false H 2 * * 0-4 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=parone;PARALLEL_RUN=true''' -CRON_SETTINGS_42 = '''H 1 * * 5 % MAVEN_PROFILE_NAME=oke-weekly-sequential;CLUSTER_NAME=seq42weekly;PARALLEL_RUN=false +CRON_SETTINGS_42 = '''H 1 * * 6 % MAVEN_PROFILE_NAME=oke-weekly-sequential;CLUSTER_NAME=seq42weekly;PARALLEL_RUN=false H 23 * * 0-4 % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false H 18 * * 0-4 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' From e4ef37d96e2866b7c56b0d7c758394f6900102ed Mon Sep 17 00:00:00 2001 From: maggie_he Date: Tue, 8 Oct 2024 15:06:39 +0000 Subject: [PATCH 185/356] Enable SSL for ItCrossDomainTransactionSecurity --- .../ItCrossDomainTransactionSecurity.java | 226 ++++++++++++++++-- .../crossdomsecurity/model.dynamic.wls.yaml | 6 + .../crossdomsecurity/san.config.template.txt | 7 + 3 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 integration-tests/src/test/resources/crossdomsecurity/san.config.template.txt diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java index 79e41c0319d..70483a9efbc 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransactionSecurity.java @@ -11,7 +11,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1HTTPIngressPath; @@ -30,6 +32,8 @@ import oracle.weblogic.kubernetes.actions.impl.Cluster; import oracle.weblogic.kubernetes.actions.impl.NginxParams; import oracle.weblogic.kubernetes.actions.impl.Service; +import oracle.weblogic.kubernetes.actions.impl.primitive.Command; +import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; @@ -40,6 +44,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; import static java.net.InetAddress.getLocalHost; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; @@ -77,6 +82,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.FileUtils.copyFileToPod; +import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createIngressAndRetryIfFail; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; @@ -118,6 +124,7 @@ class ItCrossDomainTransactionSecurity { private static String domain2ManagedServerPrefix = domainUid2 + "-managed-server"; private static LoggingFacade logger = null; private static int replicaCount = 2; + private static String clusterName = "cluster-2"; private static int t3ChannelPort1 = getNextFreePort(); private static int t3ChannelPort2 = getNextFreePort(); private static String domain1AdminExtSvcRouteHost = null; @@ -126,6 +133,11 @@ class ItCrossDomainTransactionSecurity { private static String nginxNamespace = null; private static NginxParams nginxHelmParams = null; private static int nginxNodePort; + private static Path tlsCertFile; + private static Path tlsKeyFile; + private static Path jksTrustFile; + private static String tlsSecretName = domainUid2 + "-test-tls-secret"; + private static String hostAddress = K8S_NODEPORT_HOST; @@ -135,7 +147,7 @@ class ItCrossDomainTransactionSecurity { * JUnit engine parameter resolution mechanism */ @BeforeAll - public static void initAll(@Namespaces(3) List namespaces) { + public static void initAll(@Namespaces(3) List namespaces) throws UnknownHostException { logger = getLogger(); // get a new unique opNamespace @@ -152,6 +164,7 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace list is null"); nginxNamespace = namespaces.get(2); + // Create the repo secret to pull the image // this secret is used only for non-kind cluster createTestRepoSecret(domainNamespace); @@ -167,25 +180,6 @@ public static void initAll(@Namespaces(3) List namespaces) { buildDomains(); - } - - /** - * Configure two domains d1 and d2 with CrossDomainSecurityEnabled set to true - * On both domains create a user (cross-domain) with group CrossDomainConnectors - * Add required Credential Mapping - * Deploy a JSP on d1's admin server that takes 2 parameteers - * a. The tx aaction b. the d2's cluster service url - * Starts a User transcation - * Send 10 messgaes to a distributed destination (jms.testUniformQueue) on d2 that has 2 members - * Send a message to local destination (jms.admin.adminQueue) on d1 - * Commit/rollback the transation - * Receive the messages from the distributed destination (jms.testUniformQueue) on d2 - * Receive the message from the local destination (jms.admin.adminQueue) on d1 - */ - @Test - @DisplayName("Check cross domain transaction works") - void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostException { - logger.info("2 domains with crossDomainSecurity enabled start up!"); int domain1AdminServiceNodePort = getServiceNodePort(domainNamespace, getExternalServicePodName(domain1AdminServerPodName), "default"); @@ -212,7 +206,25 @@ void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostExceptio } logger.info("hostHeader1 for domain1 is: " + hostHeader1); logger.info("hostAndPort1 for domain1 is: " + hostAndPort1); + } + /** + * Configure two domains d1 and d2 with CrossDomainSecurityEnabled set to true + * On both domains create a user (cross-domain) with group CrossDomainConnectors + * Add required Credential Mapping + * Deploy a JSP on d1's admin server that takes 2 parameteers + * a. The tx action b. the d2's cluster service url + * Starts a User transcation + * Using t3 send 10 messgaes to a distributed destination (jms.testUniformQueue) on d2 that has 2 members + * Using t3 Send a message to local destination (jms.admin.adminQueue) on d1 + * Commit/rollback the transation + * Using t3 receive the messages from the distributed destination (jms.testUniformQueue) on d2 + * Using t3 receive the message from the local destination (jms.admin.adminQueue) on d1 + */ + @Test + @DisplayName("Check cross domain transaction works") + void testCrossDomainTxWithCrossDomainSecurityEnabled() throws UnknownHostException { + // build the standalone JMS Client on Admin pod String destLocation = "/u01/JmsSendReceiveClient.java"; assertDoesNotThrow(() -> copyFileToPod(domainNamespace, @@ -294,6 +306,122 @@ void testCrossDomainTransactionCommitSecurityEnable() throws UnknownHostExceptio "Wait for JMS Client to send/recv msg"); } + /** + * Configure two domains d1 and d2 with CrossDomainSecurityEnabled set to true + * On both domains create a user (cross-domain) with group CrossDomainConnectors + * Add required Credential Mapping + * Deploy a JSP on d1's admin server that takes 2 parameteers + * a. The tx action b. the d2's cluster service url + * Starts a User transcation + * Using t3s send 10 messgaes to a distributed destination (jms.testUniformQueue) on d2 that has 2 members + * Using t3s Send a message to local destination (jms.admin.adminQueue) on d1 + * Commit/rollback the transation + * Using t3s receive the messages from the distributed destination (jms.testUniformQueue) on d2 + * Using t3s Receive the message from the local destination (jms.admin.adminQueue) on d1 + */ + @Test + @DisplayName("Check cross domain transaction works when SSL enabled") + @DisabledIfEnvironmentVariable(named = "OKE_CLUSTER", matches = "true") + void testCrossDomainTxWithCrossDomainSecurityAndSSLEnabled() throws UnknownHostException { + + // Create SSL certificate and key using openSSL with SAN extension + createCertKeyFiles(hostAddress); + // Create kubernates secret using genereated certificate and key + createSecretWithTLSCertKey(tlsSecretName); + // Import the tls certificate into a JKS truststote to be used while + // running the standalone client. + importKeytoTrustStore(); + + //In a UserTransaction send 10 msg to remote udq and 1 msg to local queue and commit the tx + StringBuffer curlCmd1 = new StringBuffer("curl -skg --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd1.append(" -H 'host: " + hostHeader1 + "' "); + } + String url1 = "\"http://" + hostAndPort1 + + "/sample_war/dtx.jsp?remoteurl=t3s://domain2-cluster-cluster-2:8500&action=commit\""; + curlCmd1.append(url1); + logger.info("Executing curl command: {0}", curlCmd1); + assertTrue(getCurlResult(curlCmd1.toString()).contains("Message sent in a commit User Transation"), + "Didn't send expected msg "); + + //receive msg from the udq that has 2 memebers + StringBuffer curlCmd2 = new StringBuffer("curl -j --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd2.append(" -H 'host: " + hostHeader1 + "' "); + } + String url2 = "\"http://" + hostAndPort1 + + "/sample_war/get.jsp?remoteurl=" + + "t3s://domain2-cluster-cluster-2:8500&action=recv&dest=jms.testUniformQueue\""; + curlCmd2.append(url2); + logger.info("Executing curl command: {0}", curlCmd2); + for (int i = 0; i < 2; i++) { + assertTrue(getCurlResult(curlCmd2.toString()).contains("Total Message(s) Received : 5"), + "Didn't receive expected msg count from remote queue"); + } + + // receive 1 msg from the local queue + logger.info("Receiving 1 msg from the local queue"); + StringBuffer curlCmdx = new StringBuffer("curl -j --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmdx.append(" -H 'host: " + hostHeader1 + "' "); + } + String urlx = "\"http://" + hostAndPort1 + + "/sample_war/get.jsp?remoteurl=" + + "t3s://domain1-admin-server:7002&action=recv&dest=jms.admin.adminQueue\""; + curlCmdx.append(urlx); + logger.info("Executing curl command for local queue: {0}", curlCmdx); + assertTrue(getCurlResult(curlCmdx.toString()).contains("Total Message(s) Received : 1"), + "Didn't receive expected msg count from local queue"); + + //In a UserTransaction send 10 msg to remote udq and 1 msg to local queue and rollback the tx + StringBuffer curlCmd3 = new StringBuffer("curl -skg --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd3.append(" -H 'host: " + hostHeader1 + "' "); + } + String url3 = "\"http://" + hostAndPort1 + + "/sample_war/dtx.jsp?remoteurl=t3s://domain2-cluster-cluster-2:8500&action=rollback\""; + curlCmd3.append(url3); + logger.info("Executing curl command: {0}", curlCmd3); + assertTrue(getCurlResult(curlCmd3.toString()).contains("Message sent in a rolled-back User Transation"), + "Didn't send expected msg "); + + //receive 0 msg from the udq that has 2 memebers + StringBuffer curlCmd4 = new StringBuffer("curl -j --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmd4.append(" -H 'host: " + hostHeader1 + "' "); + } + String url4 = "\"http://" + hostAndPort1 + + "/sample_war/get.jsp?remoteurl=" + + "t3s://domain2-cluster-cluster-2:8500&action=recv&dest=jms.testUniformQueue\""; + curlCmd4.append(url4); + logger.info("Executing curl command: {0}", curlCmd4); + for (int i = 0; i < 2; i++) { + assertTrue(getCurlResult(curlCmd4.toString()).contains("Total Message(s) Received : 0"), + "Didn't receive expected msg count from remote queue"); + } + + // receive 0 msg from the local queue + logger.info("Receiving 0 msg from the local queue"); + StringBuffer curlCmdy = new StringBuffer("curl -j --show-error --noproxy '*' "); + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlCmdy.append(" -H 'host: " + hostHeader1 + "' "); + } + String urly = "\"http://" + hostAndPort1 + + "/sample_war/get.jsp?remoteurl=" + + "t3s://domain1-admin-server:7002&action=recv&dest=jms.admin.adminQueue\""; + curlCmdy.append(urly); + logger.info("Executing curl command for local queue: {0}", curlCmdy); + assertTrue(getCurlResult(curlCmdx.toString()).contains("Total Message(s) Received : 0"), + "Didn't receive expected msg count from local queue"); + + } + private static String createAuxImage(String imageName, String imageTag, List wdtModelFile, String wdtVariableFile) { @@ -630,5 +758,63 @@ private static void createNginxIngressPathRoutingRules() { assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); } + // Create and display SSL certificate and key using openSSL with SAN extension + private static void createCertKeyFiles(String cn) { + + Map sanConfigTemplateMap = new HashMap<>(); + sanConfigTemplateMap.put("INGRESS_HOST", hostAddress); + + Path srcFile = Paths.get(RESOURCE_DIR, + "tunneling", "san.config.template.txt"); + Path targetFile = assertDoesNotThrow( + () -> generateFileFromTemplate(srcFile.toString(), + "san.config.txt", sanConfigTemplateMap)); + logger.info("Generated SAN config file {0}", targetFile); + + tlsKeyFile = Paths.get(RESULTS_ROOT, domainNamespace + "-tls.key"); + tlsCertFile = Paths.get(RESULTS_ROOT, domainNamespace + "-tls.cert"); + String opcmd = "openssl req -x509 -nodes -days 365 -newkey rsa:2048 " + + "-keyout " + tlsKeyFile + " -out " + tlsCertFile + + " -subj \"/CN=" + cn + "\" -extensions san" + + " -config " + Paths.get(RESULTS_ROOT, "san.config.txt"); + assertTrue( + Command.withParams(new CommandParams() + .command(opcmd)).execute(), "openssl req command fails"); + + String opcmd2 = "openssl x509 -in " + tlsCertFile + " -noout -text "; + assertTrue( + Command.withParams(new CommandParams() + .command(opcmd2)).execute(), "openssl list command fails"); + } + + // Import the certificate into a JKS TrustStore to be used while running + // external JMS client to send message to WebLogic. + private static void importKeytoTrustStore() { + + jksTrustFile = Paths.get(RESULTS_ROOT, domainNamespace + "-trust.jks"); + String keycmd = "keytool -import -file " + tlsCertFile + + " --keystore " + jksTrustFile + + " -storetype jks -storepass password -noprompt "; + assertTrue( + Command.withParams(new CommandParams() + .command(keycmd)).execute(), "keytool import command fails"); + + String keycmd2 = "keytool -list -keystore " + jksTrustFile + + " -storepass password -noprompt"; + assertTrue( + Command.withParams(new CommandParams() + .command(keycmd2)).execute(), "keytool list command fails"); + } + + // Create kubernetes secret from the ssl key and certificate + private static void createSecretWithTLSCertKey(String tlsSecretName) { + String kcmd = KUBERNETES_CLI + " create secret tls " + tlsSecretName + " --key " + + tlsKeyFile + " --cert " + tlsCertFile + " -n " + domainNamespace; + assertTrue( + Command.withParams(new CommandParams() + .command(kcmd)).execute(), KUBERNETES_CLI + " create secret command fails"); + } + + } diff --git a/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml b/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml index f4a4442b66a..6462e739d92 100755 --- a/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml +++ b/integration-tests/src/test/resources/crossdomsecurity/model.dynamic.wls.yaml @@ -34,6 +34,9 @@ topology: Server: "@@PROP:ADMIN_SERVER_NAME@@": ListenPort: 7001 + SSL: + Enabled: true + ListenPort: 7002 NetworkAccessPoint: T3Channel: ListenPort: '@@PROP:T3CHANNELPORT@@' @@ -43,6 +46,9 @@ topology: "@@PROP:CLUSTER_NAME@@-template": Cluster: "@@PROP:CLUSTER_NAME@@" ListenPort : '@@PROP:MANAGED_SERVER_PORT@@' + SSL: + Enabled: true + ListenPort: 8500 resources: WebAppContainer: WeblogicPluginEnabled: true diff --git a/integration-tests/src/test/resources/crossdomsecurity/san.config.template.txt b/integration-tests/src/test/resources/crossdomsecurity/san.config.template.txt new file mode 100644 index 00000000000..54c5c3557c0 --- /dev/null +++ b/integration-tests/src/test/resources/crossdomsecurity/san.config.template.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +[req] +distinguished_name=req +[san] +subjectAltName=IP:INGRESS_HOST From e2f98c76b091ccd63b9c4ff5364ebfb1a9600cd9 Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Tue, 8 Oct 2024 22:33:26 +0000 Subject: [PATCH 186/356] OWLS-121702 - [wko-nightly] ItIstioDBOperator tests are failing on release/4.2 OKE nightly --- .../kubernetes/ItIstioDBOperator.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java index 64f172f0a94..a819302f3a6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java @@ -74,6 +74,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createTestWebAppWarFile; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getServiceExtIPAddrtOke; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runClientInsidePod; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.runJavacInsidePod; @@ -139,6 +140,9 @@ class ItIstioDBOperator { private static String hostHeader; Map httpHeaders; + private static final String istioNamespace = "istio-system"; + private static final String istioIngressServiceName = "istio-ingressgateway"; + /** * Start DB service and create RCU schema. * Assigns unique namespaces for operator and domains. @@ -418,7 +422,11 @@ private void runJmsClientOnAdminPod(String action, String queue) { * @returns true if MBean is found otherwise false **/ private boolean checkJmsServerRuntime(String jmsServer, String managedServer) throws UnknownHostException { - String hostAndPort = getHostAndPort(adminSvcExtRouteHost, wlDomainIstioIngressPort); + // In internal OKE env, use Istio EXTERNAL-IP; in non-OKE env, use K8S_NODEPORT_HOST + ":" + istioIngressPort + String hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null + ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) + : getHostAndPort(adminSvcExtRouteHost, wlDomainIstioIngressPort); + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostAndPort = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + ISTIO_HTTP_HOSTPORT; } @@ -437,7 +445,10 @@ private boolean checkJmsServerRuntime(String jmsServer, String managedServer) th * @returns true if MBean is found otherwise false **/ private boolean checkStoreRuntime(String storeName, String managedServer) throws UnknownHostException { - String hostAndPort = getHostAndPort(adminSvcExtRouteHost, wlDomainIstioIngressPort); + String hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null + ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) + : getHostAndPort(adminSvcExtRouteHost, wlDomainIstioIngressPort); + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostAndPort = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + ISTIO_HTTP_HOSTPORT; } @@ -458,9 +469,13 @@ private boolean checkStoreRuntime(String storeName, String managedServer) throws * @returns true if MBean is found otherwise false **/ private boolean checkJtaRecoveryServiceRuntime(String managedServer, - String recoveryService, String active) throws UnknownHostException { + String recoveryService, + String active) throws UnknownHostException { - String hostAndPort = getHostAndPort(adminSvcExtRouteHost, wlDomainIstioIngressPort); + String hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null + ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) + : getHostAndPort(adminSvcExtRouteHost, wlDomainIstioIngressPort); + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostAndPort = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + ISTIO_HTTP_HOSTPORT; } From bdf095f0d211ea7f4ecb742628cafe2c27201002 Mon Sep 17 00:00:00 2001 From: xiancao Date: Wed, 9 Oct 2024 17:42:14 +0000 Subject: [PATCH 187/356] don't install traefik in crio-pipeline --- .../weblogic/kubernetes/extensions/InitializationTasks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index afd84b2575c..336b3daaab1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -311,7 +311,7 @@ public void beforeAll(ExtensionContext context) { //install webhook to prevent every operator installation trying to update crd installWebHookOnlyOperator("DomainOnPvSimplification=true"); //install traefik when running with podman container runtime - if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !CRIO) { installTraefikLB(); } //install Oracle Database operator as a one time task From db297feb1cec103f59c1c7f52408e563a58654b5 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Wed, 9 Oct 2024 20:06:30 +0000 Subject: [PATCH 188/356] Introduce k8s network failure reason and remove previous network related error... --- documentation/domains/Domain.json | 1 + kubernetes/crd/domain-crd.yaml | 3 +- .../operator/StuckPodProcessing.java | 2 ++ .../kubernetes/operator/WaitForReadyStep.java | 1 + .../kubernetes/operator/WebhookMain.java | 1 + .../helpers/PersistentVolumeClaimHelper.java | 2 ++ .../helpers/PersistentVolumeHelper.java | 2 ++ .../helpers/PodDisruptionBudgetHelper.java | 3 ++ .../operator/helpers/ResponseStep.java | 29 ++++++++++++++++++- .../operator/helpers/SecretHelper.java | 1 + .../operator/helpers/ServiceHelper.java | 3 ++ .../operator/helpers/WebhookHelper.java | 3 ++ .../operator/steps/DefaultResponseStep.java | 1 + .../domain/model/DomainFailureReason.java | 7 +++++ 14 files changed, 57 insertions(+), 2 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index 5217e91d0f6..d1d922c0b42 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -238,6 +238,7 @@ "DomainInvalid", "Introspection", "Kubernetes", + "KubernetesNetworkException", "ServerPod", "PersistentVolumeClaim", "ReplicasTooHigh", diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index c468105717c..fc45002df75 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 3cad99b24fb84de65dc38d7734ce269fb7058c3b6aed32b85ef590e142921635 + weblogic.sha256: 8cd5a2176fe99b104c82048d750d42f1130341bdfdba825493bc64de45025424 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -10322,6 +10322,7 @@ spec: - DomainInvalid - Introspection - Kubernetes + - KubernetesNetworkException - ServerPod - PersistentVolumeClaim - ReplicasTooHigh diff --git a/operator/src/main/java/oracle/kubernetes/operator/StuckPodProcessing.java b/operator/src/main/java/oracle/kubernetes/operator/StuckPodProcessing.java index 9d75f482c21..736d843955d 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/StuckPodProcessing.java +++ b/operator/src/main/java/oracle/kubernetes/operator/StuckPodProcessing.java @@ -64,6 +64,7 @@ public PodListProcessing(String namespace, OffsetDateTime dateTime) { @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); callResponse.getResult().getItems().stream() .filter(pod -> isStuck(pod, now)) .forEach(pod -> addStuckPodToPacket(packet, pod)); @@ -156,6 +157,7 @@ public ForcedDeleteResponseStep(String name, String namespace, String domainUID) @Override @SuppressWarnings("try") public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); try (ThreadLoggingContext ignored = ThreadLoggingContext.setThreadContext().namespace(namespace).domainUid(domainUID)) { LOGGER.info(POD_FORCE_DELETED, name, namespace); diff --git a/operator/src/main/java/oracle/kubernetes/operator/WaitForReadyStep.java b/operator/src/main/java/oracle/kubernetes/operator/WaitForReadyStep.java index ef2421c2e7a..5de5c4d0827 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/WaitForReadyStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/WaitForReadyStep.java @@ -254,6 +254,7 @@ static class MakeRightDomainStep extends DefaultResponseStep { @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); MakeRightDomainOperation makeRightDomainOperation = (MakeRightDomainOperation)packet.get(MAKE_RIGHT_DOMAIN_OPERATION); if (makeRightDomainOperation != null) { diff --git a/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java b/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java index 267453403cd..f3631d22c13 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java +++ b/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java @@ -212,6 +212,7 @@ private class CrdPresenceResponseStep extends D @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); warnedOfCrdAbsence = false; crdPresenceCheckCount.set(0); return super.onSuccess(packet, callResponse); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeClaimHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeClaimHelper.java index 70a3f586cef..064512e12d1 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeClaimHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeClaimHelper.java @@ -144,6 +144,7 @@ private NextAction updateDomainStatus(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); logPersistentVolumeClaimCreated(messageKey); addPersistentVolumeClaimToRecord(callResponse.getResult()); return doNext(packet); @@ -164,6 +165,7 @@ public NextAction onFailure(Packet packet, CallResponse @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); DomainPresenceInfo info = packet.getSpi(DomainPresenceInfo.class); V1PersistentVolumeClaim persistentVolumeClaim = callResponse.getResult(); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeHelper.java index 0969bea8739..2cfa456a33a 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PersistentVolumeHelper.java @@ -133,6 +133,7 @@ private NextAction updateDomainStatus(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); logPersistentVolumeCreated(messageKey); return doNext(packet); } @@ -152,6 +153,7 @@ public NextAction onFailure(Packet packet, CallResponse call @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); DomainPresenceInfo info = packet.getSpi(DomainPresenceInfo.class); V1PersistentVolume persistentVolume = callResponse.getResult(); if (persistentVolume == null) { diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodDisruptionBudgetHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodDisruptionBudgetHelper.java index 2ac78d276e8..e0583dfc665 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodDisruptionBudgetHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodDisruptionBudgetHelper.java @@ -113,6 +113,7 @@ private NextAction updateDomainStatus(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); logPodDisruptionBudgetCreated(messageKey); addPodDisruptionBudgetToRecord(callResponse.getResult()); return doNext(packet); @@ -133,6 +134,7 @@ public NextAction onFailure(Packet packet, CallResponse c @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); V1PodDisruptionBudget podDisruptionBudget = callResponse.getResult(); if (podDisruptionBudget == null) { removePodDisruptionBudgetFromRecord(); @@ -157,6 +159,7 @@ public NextAction onFailure(Packet packet, CallResponse c @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); logPodDisruptionBudgetPatched(); return doNext(packet); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java index ded9efb6ccc..5d46f81e530 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java @@ -3,6 +3,8 @@ package oracle.kubernetes.operator.helpers; +import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.util.Collections; import java.util.Optional; import java.util.stream.Collectors; @@ -25,6 +27,7 @@ import oracle.kubernetes.operator.work.Packet; import oracle.kubernetes.operator.work.Step; import oracle.kubernetes.weblogic.domain.model.DomainCondition; +import oracle.kubernetes.weblogic.domain.model.DomainFailureReason; import oracle.kubernetes.weblogic.domain.model.DomainResource; import static oracle.kubernetes.common.CommonConstants.CRD; @@ -37,6 +40,7 @@ import static oracle.kubernetes.operator.calls.AsyncRequestStep.accessContinue; import static oracle.kubernetes.weblogic.domain.model.DomainConditionType.FAILED; import static oracle.kubernetes.weblogic.domain.model.DomainFailureReason.KUBERNETES; +import static oracle.kubernetes.weblogic.domain.model.DomainFailureReason.KUBERNETES_NETWORK_EXCEPTION; /** * Step to receive response of Kubernetes API server call. @@ -80,6 +84,20 @@ public final void setPrevious(Step previousStep) { this.previousStep = previousStep; } + /** + * Clear out any existing Kubernetes network exception (ConnectException and SocketTimeoutException). + * + * @param packet packet + */ + public static void clearExistingKubernetesNetworkException(Packet packet) { + Optional.ofNullable(packet.getSpi(DomainPresenceInfo.class)) + .map(DomainPresenceInfo::getDomain) + .map(DomainResource::getStatus) + .ifPresent(status -> status.removeConditionsMatching( + c -> c.hasType(FAILED) && KUBERNETES_NETWORK_EXCEPTION == c.getReason())); + } + + @Override public final NextAction apply(Packet packet) { NextAction nextAction = getActionForCallResponse(packet); @@ -233,7 +251,16 @@ private void addDomainFailureStatus(Packet packet, RequestParams requestParams, private void updateFailureStatus( @Nonnull DomainResource domain, RequestParams requestParams, ApiException apiException) { - DomainCondition condition = new DomainCondition(FAILED).withFailureInfo(domain.getSpec()).withReason(KUBERNETES) + DomainFailureReason reason = KUBERNETES; + if (apiException != null) { + LOGGER.fine("updateFailureStatus: apiException: " + apiException.getCause()); + LOGGER.fine("updateFailureStatus: status code: " + apiException.getCode()); + } + if (apiException != null && (apiException.getCause() instanceof ConnectException + || apiException.getCause() instanceof SocketTimeoutException)) { + reason = DomainFailureReason.KUBERNETES_NETWORK_EXCEPTION; + } + DomainCondition condition = new DomainCondition(FAILED).withFailureInfo(domain.getSpec()).withReason(reason) .withMessage(createMessage(requestParams, apiException)); addFailureStatus(domain, condition); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/SecretHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/SecretHelper.java index ea0860124bf..a73fe223c0a 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/SecretHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/SecretHelper.java @@ -102,6 +102,7 @@ public NextAction onFailure(Packet packet, CallResponse callResponse) @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); V1Secret secret = callResponse.getResult(); packet.getSpi(DomainPresenceInfo.class).setWebLogicCredentialsSecret(secret); insertAuthorizationSource(packet, secret); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/ServiceHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/ServiceHelper.java index b4389a0759d..46c18d32d27 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/ServiceHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/ServiceHelper.java @@ -663,6 +663,7 @@ public NextAction onFailure(Packet packet, CallResponse callResponse) @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); V1Service service = callResponse.getResult(); if (service == null) { removeServiceFromRecord(); @@ -687,6 +688,7 @@ public NextAction onFailure(Packet packet, CallResponse callResponse) @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); return doNext(createReplacementService(getNext()), packet); } } @@ -714,6 +716,7 @@ private NextAction updateDomainStatus(Packet packet, CallResponse cal @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); logServiceCreated(messageKey); addServiceToRecord(callResponse.getResult()); return doNext(packet); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java index e9f7d08c19b..d0ea0226f7c 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java @@ -193,6 +193,7 @@ private class ReadResponseStep extends WebhookConfigResponseStep { @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); V1ValidatingWebhookConfiguration existingWebhookConfig = callResponse.getResult(); if (existingWebhookConfig == null) { return doNext(createValidatingWebhookConfiguration(getNext()), packet); @@ -273,6 +274,7 @@ private class CreateResponseStep extends WebhookConfigResponseStep { @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); LOGGER.info(VALIDATING_WEBHOOK_CONFIGURATION_CREATED, getName(callResponse.getResult())); return doNext(packet); } @@ -306,6 +308,7 @@ public NextAction onFailure(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); LOGGER.info(MessageKeys.VALIDATING_WEBHOOK_CONFIGURATION_REPLACED, getName(callResponse.getResult())); return doNext(packet); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/steps/DefaultResponseStep.java b/operator/src/main/java/oracle/kubernetes/operator/steps/DefaultResponseStep.java index 81abd02f27a..ec845b2535e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/steps/DefaultResponseStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/steps/DefaultResponseStep.java @@ -36,6 +36,7 @@ public NextAction onFailure(Packet packet, CallResponse callResponse) { @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { + clearExistingKubernetesNetworkException(packet); return doNext(packet); } } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainFailureReason.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainFailureReason.java index 3164c815a69..4798c1d3a89 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainFailureReason.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainFailureReason.java @@ -73,6 +73,13 @@ public String getEventError() { return KUBERNETES_EVENT_ERROR; } }, + @SerializedName("KubernetesNetworkException") + KUBERNETES_NETWORK_EXCEPTION("KubernetesNetworkException") { + @Override + public String getEventError() { + return KUBERNETES_EVENT_ERROR; + } + }, @SerializedName("ServerPod") SERVER_POD("ServerPod") { @Override From 45d4faf66aea5f6d36ef5294412eb9851ef9ddea Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 10 Oct 2024 21:27:44 +0000 Subject: [PATCH 189/356] Merge branch 'edburns-msft-ew-6237-update-scripts-zip-version' into 'main' On branch edburns-msft-ew-6237-update-scripts-zip-version Chang 4.2.5 to 4.2.8. See merge request weblogic-cloud/weblogic-kubernetes-operator!4837 (cherry picked from commit f933d6d912fe331361cfd45038f62352087070de) f780290c On branch edburns-msft-ew-6237-update-scripts-zip-version Chang 4.2.5 to 4.2.8. --- .../includes/download-samples-zip.txt | 4 ++-- .../samples/azure-kubernetes-service/model-in-image.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt index 3e4b1fefe4d..16369529d99 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt @@ -1,11 +1,11 @@ ##### Download the WebLogic Kubernetes Operator sample. -Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.2.5, but should work with the latest release. +Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.2.8, but should work with the latest release. ```shell $ cd $BASE_DIR $ mkdir sample-scripts -$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/releases/download/v4.2.5/sample-scripts.zip \ +$ curl -m 120 -fL https://github.com/oracle/weblogic-kubernetes-operator/releases/download/v4.2.8/sample-scripts.zip \ -o ${BASE_DIR}/sample-scripts/sample-scripts.zip $ unzip ${BASE_DIR}/sample-scripts/sample-scripts.zip -d ${BASE_DIR}/sample-scripts ``` diff --git a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md index 37399013a65..99319d6bec9 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md +++ b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md @@ -103,7 +103,7 @@ Update the repo to get the latest Helm charts. It is a best practice to do this $ helm repo update $ helm install weblogic-operator weblogic-operator/weblogic-operator \ --namespace sample-weblogic-operator-ns \ - --version 4.2.5 \ + --version 4.2.8 \ --set serviceAccount=sample-weblogic-operator-sa \ --wait ``` @@ -119,7 +119,7 @@ REVISION: 1 TEST SUITE: None ``` -{{% notice tip %}} If you wish to use a more recent version of the operator, replace the `4.2.5` in the preceding command with the other version number. To see the list of versions, visit the [GitHub releases page](https://github.com/oracle/weblogic-kubernetes-operator/releases). +{{% notice tip %}} If you wish to use a more recent version of the operator, replace the `4.2.8` in the preceding command with the other version number. To see the list of versions, visit the [GitHub releases page](https://github.com/oracle/weblogic-kubernetes-operator/releases). {{% /notice %}} @@ -130,7 +130,7 @@ $ helm list -A ``` ``` NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -weblogic-operator sample-weblogic-operator-ns 1 2023-05-15 10:31:05.1890341 +0800 CST deployeweblogic-operator-4.2.5 4.2.5 +weblogic-operator sample-weblogic-operator-ns 1 2023-05-15 10:31:05.1890341 +0800 CST deployeweblogic-operator-4.2.8 4.2.8 ``` ```shell $ kubectl get pods -n sample-weblogic-operator-ns From 4e171edda5797cc02a25a4bd98d9ff0289effe17 Mon Sep 17 00:00:00 2001 From: vanajakshi_mukkara Date: Fri, 11 Oct 2024 15:47:08 +0000 Subject: [PATCH 190/356] Adding Operator upgrade tests using Auxiliary domain, Mii domain and V8, V9 schema --- Jenkinsfile.oke | 2 - .../kubernetes/ItOperatorWlsUpgrade.java | 376 +++++------------- .../actions/impl/OperatorParams.java | 14 +- .../kubernetes/utils/OperatorUtils.java | 52 +++ .../auxiliary.single.image.v9.template.yaml | 66 +++ 5 files changed, 233 insertions(+), 277 deletions(-) create mode 100755 integration-tests/src/test/resources/upgrade/auxiliary.single.image.v9.template.yaml diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 3975457aa58..c6c5104d764 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -9,8 +9,6 @@ CRON_SETTINGS_42 = '''H 1 * * 6 % MAVEN_PROFILE_NAME=oke-weekly-sequential;CLUST H 23 * * 0-4 % MAVEN_PROFILE_NAME=oke-sequential;CLUSTER_NAME=seq42two;PARALLEL_RUN=false H 18 * * 0-4 % MAVEN_PROFILE_NAME=oke-parallel;CLUSTER_NAME=par42one;PARALLEL_RUN=true''' - - CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-nightly' ? CRON_SETTINGS_MAIN : CRON_SETTINGS_42}" pipeline { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java index e057a3c18ab..ebea98d6c16 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java @@ -15,17 +15,6 @@ import java.util.List; import java.util.Map; -import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1LocalObjectReference; -import io.kubernetes.client.openapi.models.V1ObjectMeta; -import oracle.weblogic.domain.AdminServer; -import oracle.weblogic.domain.AdminService; -import oracle.weblogic.domain.Channel; -import oracle.weblogic.domain.Configuration; -import oracle.weblogic.domain.DomainResource; -import oracle.weblogic.domain.DomainSpec; -import oracle.weblogic.domain.Model; -import oracle.weblogic.domain.ServerPod; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; @@ -35,18 +24,16 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_COMPLETED_TYPE; @@ -64,11 +51,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OLD_DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; -import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; -import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; -import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_IMAGE_NAME; -import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; @@ -76,7 +59,6 @@ import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; -import static oracle.weblogic.kubernetes.actions.TestActions.createDomainCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.scaleCluster; import static oracle.weblogic.kubernetes.assertions.TestAssertions.domainExists; @@ -89,7 +71,6 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; @@ -101,7 +82,6 @@ import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.getPodCreationTime; -import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static oracle.weblogic.kubernetes.utils.UpgradeUtils.checkCrdVersion; @@ -173,129 +153,90 @@ public static void init() { } /** - * Operator upgrade from 3.3.8 to current. + * Operator upgrade from 4.0.9 to current with Mii domain in V8 schema. */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 3.3.8 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom338ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom338ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "3.3.8", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); - } - - /** - * Operator upgrade from 3.4.12 to current. - */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 3.4.12 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom3412ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom3412ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "3.4.12", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + @Test + @DisplayName("Upgrade Operator from 4.0.9 to current") + void testOperatorUpgradeMiiDomainV8From409ToCurrent() { + logger.info("Starting test testOperatorUpgradeMiiDomainV8From409ToCurrent with Mii domain v8 schema"); + installOperatorCreateMiiDomainAndUpgrade("4.0.9", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } - + /** - * Operator upgrade from 3.4.13 to current. + * Upgrade Operator from 4.0.10 to current with Auxiliary image domain, V9 schema. */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 3.4.13 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom3413ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom3413ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "3.4.13", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + @Test + @DisplayName("Upgrade Operator from 4.0.10 to current") + void testOperatorUpgradeAuxDomainV9From4010ToCurrent() { + logger.info("Starting test testOperatorUpgradeAuxDomainV9From4010ToCurrent with Auxiliary domain v9 schema"); + installOperatorCreateAuxDomainAndUpgrade("4.0.10", DOMAIN_VERSION); } /** - * Operator upgrade from 4.0.8 to current. - */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 4.0.8 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom408ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom408ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.0.8", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); - } - - /** - * Operator upgrade from 4.0.9 to current. + * Upgrade Operator from 4.1.7 to current with Mii domain in V8 schema. */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 4.0.9 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom409ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom409ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.0.9", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + @Test + @DisplayName("Upgrade Operator from 4.1.7 to current") + void testOperatorUpgradeMiiDomainV8From417ToCurrent() { + logger.info("Starting test testOperatorUpgradeMiiDomainV8From417ToCurrent with Mii domain v8 schema"); + installOperatorCreateMiiDomainAndUpgrade("4.1.7", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** - * Operator upgrade from 4.0.10 to current. + * Operator upgrade from 4.1.8 to current with Auxiliary Image Domain, V9 schema. */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 4.0.10 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom4010ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom4010ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.0.10", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + @Test + @DisplayName("Upgrade Operator from 4.1.8 to current") + void testOperatorUpgradeAuxDomainV9From418ToCurrent() { + logger.info("Starting test testOperatorUpgradeAuxDomainV9From418ToCurrent to upgrade Auxiliary Domain " + + "with V9 schema to current"); + installOperatorCreateAuxDomainAndUpgrade("4.1.8", DOMAIN_VERSION); } - /** - * Operator upgrade from 4.1.7 to current. + * Operator upgrade from 4.2.7 to current with Auxiliary Image Domain, V9 schema. */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 4.1.7 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom417ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom417ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.1.7", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + @Test + @DisplayName("Upgrade Operator from 4.2.7 to current") + void testOperatorUpgradeAuxDomainV9From427ToCurrent() { + logger.info("Starting test testOperatorUpgradeAuxDomainV9From427ToCurrent to upgrade Domain with " + + "Auxiliary Image with v9 schema to current"); + installOperatorCreateAuxDomainAndUpgrade("4.2.7", DOMAIN_VERSION); } /** - * Operator upgrade from 4.1.8 to current. + * Operator upgrade from 4.2.6 to current with Model in Image Domain, V8 schema. */ - @ParameterizedTest - @DisplayName("Upgrade Operator from 4.1.8 to current") - @ValueSource(strings = { "Image", "FromModel" }) - void testOperatorWlsUpgradeFrom418ToCurrent(String domainType) { - logger.info("Starting test testOperatorWlsUpgradeFrom418ToCurrent with domain type {0}", domainType); - installAndUpgradeOperator(domainType, "4.1.8", DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); + @Test + @DisplayName("Upgrade Operator from 4.2.6 to current") + void testOperatorUpgradeMiiDomainV8From426ToCurrent() { + logger.info("Starting test testOperatorUpgradeMiiDomainV8From426ToCurrent to upgrade Domain with " + + "Auxiliary Image with v8 schema to current"); + installOperatorCreateMiiDomainAndUpgrade("4.2.6", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** - * Auxiliary Image Domain upgrade from Operator 3.4.1 to current. + * Operator upgrade from 3.4.13 to current with Auxiliary Image Domain, V8 schema. + * V9 schema is from Operator 4.0. */ @Test - @DisplayName("Upgrade 3.4.1 Auxiliary Domain(v8 schema) Image to current") - void testOperatorWlsAuxDomainUpgradeFrom341ToCurrent() { - logger.info("Starting testOperatorWlsAuxDomainUpgradeFrom341ToCurrent " + @DisplayName("Upgrade 3.4.13 Auxiliary Domain(v8 schema) Image to current") + void testOperatorUpgradeAuxDomainV8From3413ToCurrent() { + logger.info("Starting testOperatorUpgradeAuxDomainV8From3413ToCurrent " + "to upgrade Domain with Auxiliary Image with v8 schema to current"); - upgradeWlsAuxDomain("3.4.1"); + installOperatorCreateAuxDomainAndUpgrade("3.4.13", OLD_DOMAIN_VERSION); } /** - * Auxiliary Image Domain upgrade from Operator 3.4.4 to current. + * Upgrade Operator from 3.4.12 to current with Mii domain in V8 schema. */ @Test - @DisplayName("Upgrade 3.4.4 Auxiliary Domain(v8 schema) Image to current") - void testOperatorWlsAuxDomainUpgradeFrom344ToCurrent() { - logger.info("Starting testOperatorWlsAuxDomainUpgradeFrom344ToCurrent " - + "to upgrade Domain with Auxiliary Image with v8 schema to current"); - upgradeWlsAuxDomain("3.4.4"); - } - - /** - * Auxiliary Image Domain upgrade from Operator v3.3.8 to current. - * Currently we do not support AuxDomain upgrade 3.3.8 to Latest with - * independent webhook only WebLogic Operator in Latest branch. - * Temporarily disabled, re-enable after webhook not pre-created in - * InitializationTasks or the test is moved to a different test suite. - */ - @Disabled - @DisplayName("Upgrade 3.3.8 Auxiliary Domain(v8 schema) Image to current") - void testOperatorWlsAuxDomainUpgradeFrom338ToCurrent() { - logger.info("Starting test to upgrade Domain with Auxiliary Image with v8 schema to current"); - upgradeWlsAuxDomain("3.3.8"); + @DisplayName("Upgrade 3.4.12 Mii Domain(v8 schema) Image to current") + void testOperatorUpgradeMiiDomainV8From3412ToCurrent() { + logger.info("Starting testOperatorWlsAuxDomainV8UpgradeFrom3412ToCurrent " + + "to upgrade MII Domain with v8 schema to current"); + installOperatorCreateMiiDomainAndUpgrade("3.4.12", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } /** @@ -310,34 +251,40 @@ public void tearDown() { } } - void upgradeWlsAuxDomain(String oldVersion) { - logger.info("Upgrade version/{0} Auxiliary Domain(v8) to current", oldVersion); - installOldOperator(oldVersion,opNamespace,domainNamespace); + void installOperatorCreateAuxDomainAndUpgrade(String operatorVersion, String domainApiVersion) { + + logger.info("Upgrade version/{0} Auxiliary Domain(v8) to current", operatorVersion); + installOldOperator(operatorVersion, opNamespace, domainNamespace); createSecrets(); // Create the repo secret to pull base WebLogic image createBaseRepoSecret(domainNamespace); - // Creating an aux image domain with v8 version + // Creating an aux image domain with v8/v9 version final String auxiliaryImagePath = "/auxiliary"; List archiveList = Collections.singletonList(ARCHIVE_DIR + "/" + MII_BASIC_APP_NAME + ".zip"); List modelList = new ArrayList<>(); modelList.add(MODEL_DIR + "/" + MII_BASIC_WDT_MODEL_FILE); - modelList.add(MODEL_DIR + "/model.jms2.yaml"); + // modelList.add(MODEL_DIR + "/model.jms2.yaml"); logger.info("creating auxiliary image {0}:{1} using imagetool.sh ", miiAuxiliaryImage, MII_BASIC_IMAGE_TAG); createPushAuxiliaryImageWithDomainConfig(MII_AUXILIARY_IMAGE_NAME, miiAuxiliaryImageTag, archiveList, modelList); - // Generate a v8 version of domain.yaml file from a template file + // Generate a v8/v9 version of domain.yaml file from a template file // by replacing domain namespace, domain uid, base image and aux image String auxImage = MII_AUXILIARY_IMAGE_NAME + ":" + miiAuxiliaryImageTag; + String auxImageDomainYaml = "auxilary.single.image.template.yaml"; Map templateMap = new HashMap<>(); templateMap.put("DOMAIN_NS", domainNamespace); templateMap.put("DOMAIN_UID", domainUid); templateMap.put("AUX_IMAGE", auxImage); templateMap.put("BASE_IMAGE", WEBLOGIC_IMAGE_TO_USE_IN_SPEC); - templateMap.put("API_VERSION", "v8"); + templateMap.put("API_VERSION", domainApiVersion); + templateMap.put("CLUSTER_VERSION", CLUSTER_VERSION); + if (domainApiVersion.equals(DOMAIN_VERSION)) { + auxImageDomainYaml = "auxiliary.single.image.v9.template.yaml"; + } Path srcDomainFile = Paths.get(RESOURCE_DIR, - "upgrade", "auxilary.single.image.template.yaml"); + "upgrade", auxImageDomainYaml); Path targetDomainFile = assertDoesNotThrow( () -> generateFileFromTemplate(srcDomainFile.toString(), "domain.yaml", templateMap)); @@ -354,7 +301,7 @@ void upgradeWlsAuxDomain(String oldVersion) { // wait for the domain to exist logger.info("Checking for domain custom resource in namespace {0}", domainNamespace); testUntil( - domainExists(domainUid, "v8", domainNamespace), + domainExists(domainUid, domainApiVersion, domainNamespace), logger, "domain {0} to be created in namespace {1}", domainUid, @@ -369,17 +316,20 @@ void upgradeWlsAuxDomain(String oldVersion) { // verify there is no status condition type Completed // before upgrading to Latest verifyDomainStatusConditionTypeDoesNotExist(domainUid, domainNamespace, - DOMAIN_STATUS_CONDITION_COMPLETED_TYPE, OLD_DOMAIN_VERSION); + DOMAIN_STATUS_CONDITION_COMPLETED_TYPE, domainApiVersion); upgradeOperatorToCurrent(opNamespace); checkDomainStatus(domainNamespace,domainUid); verifyPodsNotRolled(domainNamespace, pods); - scaleClusterUpAndDown(); + scaleClusterUpAndDown(domainApiVersion); } // After upgrade scale up/down the cluster - private void scaleClusterUpAndDown() { + private void scaleClusterUpAndDown(String domainApiVersion) { String clusterName = domainUid + "-" + "cluster-1"; + if (domainApiVersion.equals(DOMAIN_VERSION)) { + clusterName = "cluster-1"; + } logger.info("Updating the cluster {0} replica count to 3", clusterName); boolean p1Success = scaleCluster(clusterName, domainNamespace,3); assertTrue(p1Success, @@ -393,8 +343,7 @@ private void scaleClusterUpAndDown() { clusterName, domainNamespace)); } - private void installDomainResource( - String domainType, + private void installMiiDomainResource( String domainVersion, String externalServiceNameSuffix) { @@ -404,27 +353,25 @@ private void installDomainResource( domainUid, domainNamespace); // create WLS domain and verify - createWlsDomainAndVerifyByDomainYaml(domainType, domainNamespace, externalServiceNameSuffix); + createWlsMiiDomainByDomainYamlAndVerify(domainNamespace, externalServiceNameSuffix, domainVersion); } // Since Operator version 3.1.0 the service pod prefix has been changed // from -external to -ext e.g. // domain1-adminserver-ext NodePort 10.96.46.242 30001:30001/TCP - private void installAndUpgradeOperator(String domainType, - String operatorVersion, String domainVersion, - String externalServiceNameSuffix) { + private void installOperatorCreateMiiDomainAndUpgrade(String operatorVersion, String domainVersion, + String externalServiceNameSuffix) { installOldOperator(operatorVersion,opNamespace,domainNamespace); - // create WLS domain and verify - installDomainResource(domainType, domainVersion, externalServiceNameSuffix); + installMiiDomainResource(domainVersion, externalServiceNameSuffix); // upgrade to current operator - upgradeOperatorAndVerify(opNamespace, domainNamespace); + upgradeOperatorAndVerify(opNamespace, domainNamespace, domainVersion); } - private void upgradeOperatorAndVerify(String opNamespace, String domainNamespace) { + private void upgradeOperatorAndVerify(String opNamespace, String domainNamespace, String domainVersion) { String opServiceAccount = opNamespace + "-sa"; String appName = "testwebapp.war"; @@ -479,7 +426,7 @@ private void upgradeOperatorAndVerify(String opNamespace, String domainNamespace "Application was not always available when the operator was getting upgraded"); } } - scaleClusterUpAndDown(); + scaleClusterUpAndDown(domainVersion); // check CRD version is updated logger.info("Checking CRD version"); @@ -509,32 +456,6 @@ private void createSecrets() { ENCRYPION_USERNAME_DEFAULT, ENCRYPION_PASSWORD_DEFAULT); } - private void createWlsDomainAndVerify(String domainType, - String domainNamespace, String domainVersion, - String externalServiceNameSuffix) { - - createSecrets(); - - String domainImage = ""; - if (domainType.equalsIgnoreCase("Image")) { - domainImage = WDT_BASIC_IMAGE_NAME + ":" + WDT_BASIC_IMAGE_TAG; - } else { - domainImage = MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG; - } - - // create domain - createDomainResource(domainNamespace, domainVersion, - domainType, domainImage); - checkDomainStarted(domainUid, domainNamespace); - logger.info("Getting node port for default channel"); - int serviceNodePort = assertDoesNotThrow(() -> getServiceNodePort( - domainNamespace, getExternalServicePodName(adminServerPodName, externalServiceNameSuffix), "default"), - "Getting admin server node port failed"); - logger.info("Validating WebLogic admin server access by login to console"); - verifyAdminConsoleAccessible(domainNamespace, K8S_NODEPORT_HOST, - String.valueOf(serviceNodePort), false); - } - private void checkDomainStarted(String domainUid, String domainNamespace) { // verify admin server pod is ready checkPodReady(adminServerPodName, domainUid, domainNamespace); @@ -595,105 +516,17 @@ private void restartDomain(String domainUid, String domainNamespace) { checkDomainStarted(domainUid, domainNamespace); } - private void createDomainResource( - String domainNamespace, - String domVersion, - String domainHomeSourceType, - String domainImage) { - - String domApiVersion = "weblogic.oracle/" + domVersion; - logger.info("Default Domain API version {0}", DOMAIN_API_VERSION); - logger.info("Domain API version selected {0}", domApiVersion); - logger.info("Domain Image name selected {0}", domainImage); - logger.info("Create domain resource for domainUid {0} in namespace {1}", - domainUid, domainNamespace); - - // create encryption secret - logger.info("Create encryption secret"); - String encryptionSecretName = "encryptionsecret"; - createSecretWithUsernamePassword(encryptionSecretName, domainNamespace, - "weblogicenc", "weblogicenc"); - DomainResource domain = new DomainResource() - .apiVersion(domApiVersion) - .kind("Domain") - .metadata(new V1ObjectMeta() - .name(domainUid) - .namespace(domainNamespace)) - .spec(new DomainSpec() - .domainUid(domainUid) - .domainHomeSourceType(domainHomeSourceType) - .image(domainImage) - .addImagePullSecretsItem(new V1LocalObjectReference() - .name(TEST_IMAGES_REPO_SECRET_NAME)) - .webLogicCredentialsSecret(new V1LocalObjectReference() - .name(adminSecretName)) - .includeServerOutInPodLog(true) - .serverStartPolicy("weblogic.oracle/v8".equals(domApiVersion) ? "IF_NEEDED" : "IfNeeded") - .serverPod(new ServerPod() - .addEnvItem(new V1EnvVar() - .name("JAVA_OPTIONS") - .value(SSL_PROPERTIES)) - .addEnvItem(new V1EnvVar() - .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom "))) - .adminServer(new AdminServer() - .adminService(new AdminService() - .addChannelsItem(new Channel() - .channelName("default") - .nodePort(getNextFreePort())))) - .configuration(new Configuration() - .model(new Model() - .runtimeEncryptionSecret(encryptionSecretName) - .domainType("WLS")) - .introspectorJobActiveDeadlineSeconds(3000L))); - boolean domCreated = assertDoesNotThrow(() -> createDomainCustomResource(domain, domVersion), - String.format("Create domain custom resource failed with ApiException for %s in namespace %s", - domainUid, domainNamespace)); - assertTrue(domCreated, - String.format("Create domain custom resource failed with ApiException " - + "for %s in namespace %s", domainUid, domainNamespace)); - setPodAntiAffinity(domain); - removePortForwardingAttribute(domainNamespace,domainUid); - } - - // Remove the artifact adminChannelPortForwardingEnabled from domain resource - // if exist, so that the Operator release default will be effective. - // e.g. in Release 3.3.x the default is false, but 4.x.x onward it is true - // However in release(s) lower to 3.3.x, the CRD does not contain this attribute - // so the patch command to remove this attribute fails. So we do not assert - // the result of patch command - // assertTrue(result, "Failed to remove PortForwardingAttribute"); - private void removePortForwardingAttribute( - String domainNamespace, String domainUid) { - - StringBuffer patchStr = new StringBuffer("[{"); - patchStr.append("\"op\": \"remove\",") - .append(" \"path\": \"/spec/adminServer/adminChannelPortForwardingEnabled\"") - .append("}]"); - logger.info("The patch String {0}", patchStr); - StringBuffer commandStr = new StringBuffer(KUBERNETES_CLI + " patch domain "); - commandStr.append(domainUid) - .append(" -n " + domainNamespace) - .append(" --type 'json' -p='") - .append(patchStr) - .append("'"); - logger.info("The Command String: {0}", commandStr); - CommandParams params = new CommandParams().defaults(); - - params.command(new String(commandStr)); - boolean result = Command.withParams(params).execute(); - } - /** * Replace the fields in domain yaml file with testing attributes. * For example, namespace, domainUid, and image. Then create domain using * KUBERNETES_CLI and verify the domain is created - * @param domainType either domain in image(Image) or model in image (FromModel) - * @param domainNamespace namespace in which to create domain + * + * @param domainNamespace namespace in which to create domain * @param externalServiceNameSuffix suffix of externalServiceName + * @param domainVersion version of the domain */ - private void createWlsDomainAndVerifyByDomainYaml(String domainType, - String domainNamespace, String externalServiceNameSuffix) { + private void createWlsMiiDomainByDomainYamlAndVerify(String domainNamespace, + String externalServiceNameSuffix, String domainVersion) { // Create the repo secret to pull the image // this secret is used only for non-kind cluster @@ -705,13 +538,13 @@ private void createWlsDomainAndVerifyByDomainYaml(String domainType, Paths.get(RESULTS_ROOT + "/" + this.getClass().getSimpleName())), String.format("Could not create directory under %s", RESULTS_ROOT)); - if (domainType.equalsIgnoreCase("Image")) { - logger.info("Domain home in image domain will be created "); - srcDomainYaml = Paths.get(RESOURCE_DIR, "domain", "domain-v8.yaml"); + if (domainVersion.equals(DOMAIN_VERSION)) { + logger.info("Model in image domain using " + DOMAIN_VERSION + " will be created "); + srcDomainYaml = Paths.get(RESOURCE_DIR, "domain", "mii-cluster-domain.yaml"); destDomainYaml = Paths.get(RESULTS_ROOT + "/" + this.getClass().getSimpleName() + "/" + "domain.yaml"); assertDoesNotThrow(() -> Files.copy(srcDomainYaml, destDomainYaml, REPLACE_EXISTING), - "File copy failed for domain-v8.yaml"); + "File copy failed for mii-cluster-domain.yaml"); } else { logger.info("Model in image domain will be created "); srcDomainYaml = Paths.get(RESOURCE_DIR, "domain", "mii-domain-v8.yaml"); @@ -731,24 +564,19 @@ private void createWlsDomainAndVerifyByDomainYaml(String domainType, assertDoesNotThrow(() -> replaceStringInFile( destDomainYaml.toString(), "domain1-weblogic-credentials", adminSecretName), "Could not modify the webLogicCredentialsSecret in the domain.yaml file"); - if (domainType.equalsIgnoreCase("Image")) { - assertDoesNotThrow(() -> replaceStringInFile( - destDomainYaml.toString(), "domain-home-in-image:14.1.1.0", - WDT_BASIC_IMAGE_NAME + ":" + WDT_BASIC_IMAGE_TAG), - "Could not modify image name in the domain.yaml file"); - } else { - assertDoesNotThrow(() -> replaceStringInFile( - destDomainYaml.toString(), "domain1-runtime-encryption-secret", encryptionSecretName), - "Could not modify runtimeEncryptionSecret in the domain-v8.yaml file"); - assertDoesNotThrow(() -> replaceStringInFile( - destDomainYaml.toString(), "model-in-image:WLS-v1", - MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG), - "Could not modify image name in the mii-domain-v8.yaml file"); - } - assertTrue(Command + assertDoesNotThrow(() -> replaceStringInFile( + destDomainYaml.toString(), "domain1-runtime-encryption-secret", encryptionSecretName), + "Could not modify runtimeEncryptionSecret in the domain.yaml file"); + assertDoesNotThrow(() -> replaceStringInFile( + destDomainYaml.toString(), "model-in-image:WLS-v1", + MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG), + "Could not modify image name in the domain.yaml file"); + + boolean result = Command .withParams(new CommandParams() .command(KUBERNETES_CLI + " create -f " + destDomainYaml)) - .execute(), KUBERNETES_CLI + " create failed"); + .execute(); + assertTrue(result, KUBERNETES_CLI + " create failed"); verifyDomain(domainUid, domainNamespace, externalServiceNameSuffix); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/OperatorParams.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/OperatorParams.java index aaa7e87b10c..419ee578104 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/OperatorParams.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/OperatorParams.java @@ -38,6 +38,7 @@ public class OperatorParams { private static final String KUBERNETES_PLATFORM = "kubernetesPlatform"; private static final String CREATE_LOGSTASH_CONFIGMAP = "createLogStashConfigMap"; private static final String WEBHOOK_ONLY = "webhookOnly"; + private static final String OPERATOR_ONLY = "operatorOnly"; private static final String CPU_REQUESTS = "cpuRequests"; private static final String CPU_LIMITS = "cpuLimits"; private static final String MEMORY_REQUESTS = "memoryRequests"; @@ -70,6 +71,9 @@ public class OperatorParams { private String kubernetesPlatform; private boolean createLogStashConfigMap = true; private boolean webhookOnly; + + private boolean operatorOnly; + private boolean openshiftIstioInjection; private String cpuRequests; private String memoryRequests; @@ -248,6 +252,11 @@ public OperatorParams webHookOnly(boolean webhookOnly) { return this; } + public OperatorParams operatorOnly(boolean operatorOnly) { + this.operatorOnly = operatorOnly; + return this; + } + /** * Loads Helm values into a value map. * @return Map of values @@ -300,7 +309,10 @@ public Map getValues() { } if (webhookOnly) { values.put(WEBHOOK_ONLY, webhookOnly); - } + } + if (operatorOnly) { + values.put(OPERATOR_ONLY, operatorOnly); + } values.put(CREATE_LOGSTASH_CONFIGMAP, createLogStashConfigMap); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OperatorUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OperatorUtils.java index df73ef73648..84146d4e620 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OperatorUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OperatorUtils.java @@ -358,7 +358,9 @@ public static OperatorParams installAndVerifyOperator(String opNamespace, domainNamespaceSelector, enableClusterRoleBinding, loggingLevel, + null, false, + true, domainNamespace); } @@ -438,6 +440,53 @@ public static OperatorParams installAndVerifyOperator(String opNamespace, String featureGates, boolean webhookOnly, String... domainNamespace) { + + return installAndVerifyOperator(opNamespace, opServiceAccount, withExternalRestAPI, externalRestHttpsPort, + opHelmParams, elasticSearchHost, elkIntegrationEnabled, createLogStashConfigMap, domainNamespaceSelectionStrategy, + domainNamespaceSelector, enableClusterRoleBinding, loggingLevel, featureGates, webhookOnly, false, + domainNamespace); + } + + /** + * Install WebLogic operator and wait up to five minutes until the operator pod is ready. + * + * @param opNamespace the operator namespace in which the operator will be installed + * @param opServiceAccount the service account name for operator + * @param withExternalRestAPI whether to use REST API + * @param externalRestHttpsPort the node port allocated for the external operator REST HTTPS interface + * @param opHelmParams the Helm parameters to install operator + * @param elasticSearchHost Elasticsearchhost + * @param elkIntegrationEnabled true to enable ELK Stack, false otherwise + * @param createLogStashConfigMap boolean indicating creating logstash + * @param domainNamespaceSelectionStrategy SelectLabel, RegExp or List, value to tell the operator + * how to select the set of namespaces that it will manage + * @param domainNamespaceSelector the label or expression value to manage namespaces + * @param enableClusterRoleBinding operator cluster role binding + * @param loggingLevel logging level of operator + * @param featureGates new feature gates string + * @param webhookOnly boolean indicating install webHookOnly operator + * @param operatorOnly boolean indicating install only Operator, webhook is not installed + * @param domainNamespace the list of the domain namespaces which will be managed by the operator + * @return the operator Helm installation parameters + */ + public static OperatorParams installAndVerifyOperator(String opNamespace, + String opServiceAccount, + boolean withExternalRestAPI, + int externalRestHttpsPort, + HelmParams opHelmParams, + String elasticSearchHost, + boolean elkIntegrationEnabled, + boolean createLogStashConfigMap, + String domainNamespaceSelectionStrategy, + String domainNamespaceSelector, + boolean enableClusterRoleBinding, + String loggingLevel, + String featureGates, + boolean webhookOnly, + boolean operatorOnly, + String... domainNamespace) { + + String operatorImage; LoggingFacade logger = getLogger(); @@ -479,6 +528,9 @@ public static OperatorParams installAndVerifyOperator(String opNamespace, if (webhookOnly) { opParams.webHookOnly(webhookOnly); } + if (operatorOnly) { + opParams.operatorOnly(operatorOnly); + } if (domainNamespaceSelectionStrategy != null) { opParams.domainNamespaceSelectionStrategy(domainNamespaceSelectionStrategy); diff --git a/integration-tests/src/test/resources/upgrade/auxiliary.single.image.v9.template.yaml b/integration-tests/src/test/resources/upgrade/auxiliary.single.image.v9.template.yaml new file mode 100755 index 00000000000..a5d523ee678 --- /dev/null +++ b/integration-tests/src/test/resources/upgrade/auxiliary.single.image.v9.template.yaml @@ -0,0 +1,66 @@ +apiVersion: "weblogic.oracle/API_VERSION" +kind: Domain +metadata: + name: DOMAIN_UID + namespace: DOMAIN_NS + labels: + weblogic.domainUID: DOMAIN_UID +spec: + domainHomeSourceType: FromModel + image: "BASE_IMAGE" + imagePullPolicy: IfNotPresent + webLogicCredentialsSecret: + name: weblogic-credentials + includeServerOutInPodLog: true + dataHome: "" + serverStartPolicy: "IfNeeded" + serverPod: + env: + - name: JAVA_OPTIONS + value: "-Dweblogic.security.TrustKeyStore=DemoTrust -Dweblogic.security.SSL.ignoreHostnameVerification=true" + - name: USER_MEM_ARGS + value: "-Djava.security.egd=file:/dev/./urandom -Xms256m -Xmx512m " + adminServer: + adminService: + channels: + - channelName: default + nodePort: 0 + - channelName: T3Channel + clusters: + - name: cluster-1 + configuration: + model: + domainType: WLS + auxiliaryImages: + - image: "AUX_IMAGE" + sourceModelHome: "/auxiliary/models" + sourceWDTInstallHome: "/auxiliary/weblogic-deploy" + runtimeEncryptionSecret: encryptionsecret + +--- + +apiVersion: "weblogic.oracle/CLUSTER_VERSION" +kind: Cluster +metadata: + name: cluster-1 + namespace: DOMAIN_NS +spec: +# The desired behavior for starting a specific cluster's member servers + clusterName: cluster-1 + serverPod: + # Instructs Kubernetes scheduler to prefer nodes for new cluster members + # where there are not already members of the same cluster. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "weblogic.clusterName" + operator: In + values: + - $(CLUSTER_NAME) + topologyKey: "kubernetes.io/hostname" + # The number of managed servers to start for this cluster + replicas: 2 From d3c4c118fc950731380a752dd992b94ae99521c8 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 11 Oct 2024 19:27:31 +0000 Subject: [PATCH 191/356] gating test suite changes --- Jenkinsfile | 189 +++++++++++++----- .../kubernetes/ItCrossDomainTransaction.java | 1 + .../kubernetes/ItEvictedPodsCycling.java | 1 + .../kubernetes/ItExternalLbTunneling.java | 1 + .../weblogic/kubernetes/ItFmwDomainOnPV.java | 1 - .../kubernetes/ItHorizontalPodAutoscaler.java | 1 + .../ItIstioProductionSecureMode.java | 1 + .../kubernetes/ItKubernetesDomainEvents.java | 1 - .../kubernetes/ItManageNameSpace.java | 3 +- .../kubernetes/ItMiiAuxiliaryImage.java | 1 - .../kubernetes/ItMiiDynamicUpdatePart1.java | 1 - .../kubernetes/ItMiiDynamicUpdatePart2.java | 1 - .../ItMonitoringExporterWebApp.java | 1 + .../kubernetes/ItMultiDomainModels.java | 2 +- .../ItServerStartPolicyConfigCluster.java | 1 - .../weblogic/kubernetes/ItT3Channel.java | 1 + .../weblogic/kubernetes/ItWlsDomainOnPV.java | 1 - 17 files changed, 148 insertions(+), 60 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e677e2786a4..d94c2f442ee 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,27 +2,25 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // def kind_k8s_map = [ - '0.19.0': [ - '1.27.1': 'kindest/node:v1.27.1@sha256:b7d12ed662b873bd8510879c1846e87c7e676a79fefc93e17b2a52989d3ff42b', - '1.27': 'kindest/node:v1.27.1@sha256:b7d12ed662b873bd8510879c1846e87c7e676a79fefc93e17b2a52989d3ff42b', - '1.26.4': 'kindest/node:v1.26.4@sha256:f4c0d87be03d6bea69f5e5dc0adb678bb498a190ee5c38422bf751541cebe92e', - '1.26': 'kindest/node:v1.26.4@sha256:f4c0d87be03d6bea69f5e5dc0adb678bb498a190ee5c38422bf751541cebe92e', - '1.25.9': 'kindest/node:v1.25.9@sha256:c08d6c52820aa42e533b70bce0c2901183326d86dcdcbedecc9343681db45161', - '1.25': 'kindest/node:v1.25.9@sha256:c08d6c52820aa42e533b70bce0c2901183326d86dcdcbedecc9343681db45161', - '1.24.13': 'kindest/node:v1.24.13@sha256:cea86276e698af043af20143f4bf0509e730ec34ed3b7fa790cc0bea091bc5dd', - '1.24': 'kindest/node:v1.24.13@sha256:cea86276e698af043af20143f4bf0509e730ec34ed3b7fa790cc0bea091bc5dd', - '1.23.17': 'kindest/node:v1.23.17@sha256:f77f8cf0b30430ca4128cc7cfafece0c274a118cd0cdb251049664ace0dee4ff', - '1.23': 'kindest/node:v1.23.17@sha256:f77f8cf0b30430ca4128cc7cfafece0c274a118cd0cdb251049664ace0dee4ff', - '1.22.17': 'kindest/node:v1.22.17@sha256:9af784f45a584f6b28bce2af84c494d947a05bd709151466489008f80a9ce9d5', - '1.22': 'kindest/node:v1.22.17@sha256:9af784f45a584f6b28bce2af84c494d947a05bd709151466489008f80a9ce9d5', - '1.21.14': 'kindest/node:v1.21.14@sha256:220cfafdf6e3915fbce50e13d1655425558cb98872c53f802605aa2fb2d569cf', - '1.21': 'kindest/node:v1.21.14@sha256:220cfafdf6e3915fbce50e13d1655425558cb98872c53f802605aa2fb2d569cf' + '0.23.0': [ + '1.30.0': 'kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e', + '1.30': 'kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e', + '1.29.4': 'kindest/node:v1.29.4@sha256:3abb816a5b1061fb15c6e9e60856ec40d56b7b52bcea5f5f1350bc6e2320b6f8', + '1.29': 'kindest/node:v1.29.4@sha256:3abb816a5b1061fb15c6e9e60856ec40d56b7b52bcea5f5f1350bc6e2320b6f8', + '1.28.9': 'kindest/node:v1.28.9@sha256:dca54bc6a6079dd34699d53d7d4ffa2e853e46a20cd12d619a09207e35300bd0', + '1.28': 'kindest/node:v1.28.9@sha256:dca54bc6a6079dd34699d53d7d4ffa2e853e46a20cd12d619a09207e35300bd0', + '1.27.13': 'kindest/node:v1.27.13@sha256:17439fa5b32290e3ead39ead1250dca1d822d94a10d26f1981756cd51b24b9d8', + '1.27': 'kindest/node:v1.27.13@sha256:17439fa5b32290e3ead39ead1250dca1d822d94a10d26f1981756cd51b24b9d8', + '1.26.15': 'kindest/node:v1.26.15@sha256:84333e26cae1d70361bb7339efb568df1871419f2019c80f9a12b7e2d485fe19', + '1.26': 'kindest/node:v1.26.15@sha256:84333e26cae1d70361bb7339efb568df1871419f2019c80f9a12b7e2d485fe19', + '1.25.16': 'kindest/node:v1.25.16@sha256:5da57dfc290ac3599e775e63b8b6c49c0c85d3fec771cd7d55b45fae14b38d3b', + '1.25': 'kindest/node:v1.25.16@sha256:5da57dfc290ac3599e775e63b8b6c49c0c85d3fec771cd7d55b45fae14b38d3b' ] ] def _kind_image = null pipeline { - agent { label 'large' } + agent { label 'large-ol9u4' } options { timeout(time: 800, unit: 'MINUTES') } @@ -67,30 +65,32 @@ pipeline { description: 'Comma separated list of individual It test classes to be run e.g., ItParameterizedDomain, ItMiiUpdateDomainConfig, ItMiiDynamicUpdate*, ItMiiMultiMode', defaultValue: '' ) + string(name: 'OPERATOR_LOG_LEVEL', + description: 'The default log level is not set', + defaultValue: '' + ) choice(name: 'KIND_VERSION', description: 'Kind version.', choices: [ - '0.19.0' + '0.23.0' ] ) choice(name: 'KUBE_VERSION', - description: 'Kubernetes version. Supported values depend on the Kind version. Kind 0.18.0: 1.26, 1.26.3, 1.25, 1.25.8, 1.24, 1.24.12, 1.23, 1.23.17, 1.22, 1.22.17, 1.21, and 1.21.14. Kind 0.17.0: 1.25, 1.25.3, 1.24, 1.24.7, 1.23, 1.23.13, 1.22, 1.22.15, 1.21, 1.21.14, 1.20, and 1.20.15. Kind 0.16.0: 1.25, 1.25.2, 1.24, 1.24.6, 1.23, 1.23.12, 1.22, 1.22.15, 1.21, 1.21.14, 1.20, and 1.20.15. Kind 0.15.0: 1.25, 1.25.0, 1.24, 1.24.4, 1.23, 1.23.10, 1.22, 1.22.13, 1.21, 1.21.14, 1.20, and 1.20.15 ', + description: 'Kubernetes version. Supported values depend on the Kind version. Kind 0.23.0: 1.30, 1.30.0, 1.29, 1.29.4, 1.28, 1.28.9, 1.27, 1.27.13, 1.26, 1.26.15, 1.25, 1.25.16 ', choices: [ // The first item in the list is the default value... - '1.27.1', + '1.30.0', + '1.30', + '1.29.4', + '1.29', + '1.28.9', + '1.28', + '1.27.13', '1.27', - '1.26.4', + '1.26.15', '1.26', - '1.25.9', - '1.25', - '1.24.13', - '1.24', - '1.23.17', - '1.23', - '1.22.17', - '1.22', - '1.21.14', - '1.21' + '1.25.16', + '1.25' ] ) string(name: 'HELM_VERSION', @@ -100,6 +100,7 @@ pipeline { choice(name: 'ISTIO_VERSION', description: 'Istio version', choices: [ + '1.23.0', '1.17.2', '1.16.1', '1.13.2', @@ -108,7 +109,7 @@ pipeline { '1.10.4', '1.9.9' ] - ) + ) booleanParam(name: 'PARALLEL_RUN', description: 'Runs tests in parallel. Default is true, test classes run in parallel.', defaultValue: true @@ -167,11 +168,11 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.0.7' + defaultValue: '2.1.2' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', - defaultValue: '15.2.0' + defaultValue: '17.0.0' ) string(name: 'GRAFANA_CHART_VERSION', description: '', @@ -211,7 +212,7 @@ pipeline { java -version mvn --version python --version - docker version + podman version ulimit -a ulimit -aH ''' @@ -351,14 +352,22 @@ pipeline { steps { sh ''' export PATH=${runtime_path} + export KIND_EXPERIMENTAL_PROVIDER=podman + + podman version + cat /etc/systemd/system/user@.service.d/delegate.conf + cat /etc/modules-load.d/iptables.conf + lsmod|grep -E "^ip_tables|^iptable_filter|^iptable_nat|^ip6" + if kind delete cluster --name ${kind_name} --kubeconfig "${kubeconfig_file}"; then echo "Deleted orphaned kind cluster ${kind_name}" fi + # settings needed by elastic logging tests + echo "running sudo sysctl -w vm.max_map_count=262144" + sudo sysctl -w vm.max_map_count=262144 cat <> ${WORKSPACE}/.mvn/maven.config fi + echo "-Dmaven.wagon.http.retryHandler.count=3" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.wle.download.url=\"${wle_download_url}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.result.root=\"${result_root}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.pv.root=\"${pv_root}\"" >> ${WORKSPACE}/.mvn/maven.config @@ -469,13 +554,14 @@ EOF echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.grafana.chart.version=\"${GRAFANA_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.collect.logs.on.success=\"${COLLECT_LOGS_ON_SUCCESS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DWLSIMG_BUILDER=\"podman\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config - echo "-Djdk.httpclient.allowRestrictedHeaders=\"host\"" >> ${WORKSPACE}/.mvn/maven.config - + echo "-Djdk.httpclient.allowRestrictedHeaders=\"host\"" >> ${WORKSPACE}/.mvn/maven.config echo "${WORKSPACE}/.mvn/maven.config contents:" cat "${WORKSPACE}/.mvn/maven.config" cp "${WORKSPACE}/.mvn/maven.config" "${result_root}" + kubectl describe node kind-worker ''' withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { withCredentials([ @@ -563,3 +649,4 @@ EOF } } } + diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index e689451ad3c..3b484bd249d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -427,6 +427,7 @@ private static void buildApplicationsAndDomains() throws UnknownHostException { * participants, then the transaction should complete successfully */ @Test + @Tag("gate") @DisplayName("Check cross domain transaction works") void testCrossDomainTransaction() throws IOException, InterruptedException { String url = String.format("http://%s/TxForward/TxForward?urls=t3://%s.%s:7001," diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java index f0e05b16ce2..acc7c24ce59 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItEvictedPodsCycling.java @@ -55,6 +55,7 @@ @Tag("olcne-mrg") @Tag("kind-parallel") @Tag("oke-parallel") +@Tag("gate") class ItEvictedPodsCycling { // constants for Domain diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java index 26120f14d09..34fea4ed77e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItExternalLbTunneling.java @@ -116,6 +116,7 @@ @IntegrationTest @DisabledOnSlimImage @Tag("olcne-mrg") +@Tag("gate") class ItExternalLbTunneling { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java index 7e5dbc239a6..88f643b7101 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPV.java @@ -180,7 +180,6 @@ public static void initAll(@Namespaces(3) List namespaces) { @Test @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") @DisplayName("Create a FMW domain on PV using simplified feature, Operator creates PV/PVC/RCU/Domain") - @Tag("gate") void testOperatorCreatesPvPvcRcuDomain() { String domainUid = "jrfonpv-simplified"; final String pvName = getUniqueName(domainUid + "-pv-"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscaler.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscaler.java index 14e3d561f0d..cfb30b5c2be 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscaler.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscaler.java @@ -65,6 +65,7 @@ @DisplayName("Test to a create MII domain and test autoscaling using HPA") @IntegrationTest @Tag("kind-parallel") +@Tag("gate") public class ItHorizontalPodAutoscaler { private static String domainNamespace = null; static int replicaCount = 2; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java index 67c43a7dd95..75e9c1b9432 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioProductionSecureMode.java @@ -68,6 +68,7 @@ @Tag("oke-arm") @Tag("olcne-mrg") @Tag("oke-parallel") +@Tag("gate") class ItIstioProductionSecureMode { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index b82aabea4e1..1a68015b434 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -237,7 +237,6 @@ public static void initAll(@Namespaces(6) List namespaces) { */ @Test @DisplayName("Test domain events for various successful domain life cycle changes") - @Tag("gate") void testK8SEventsSuccess() { OffsetDateTime timestamp = now(); logger.info("Creating domain"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java index 67358f35031..9137dd4cf02 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItManageNameSpace.java @@ -193,8 +193,8 @@ public void tearDownAll() { @Order(1) @DisplayName("install operator helm chart and domain, " + " using expression namespace management") - @Tag("gate") @Tag("crio") + @Tag("gate") void testNameSpaceManageByRegularExpression() { //create domain namespace String manageByExp1NS = "test-" + domainNamespaces[0]; @@ -272,6 +272,7 @@ void testNameSpaceManageByRegularExpression() { @Order(2) @DisplayName("install operator helm chart and domain, " + " using label namespace management") + @Tag("gate") void testNameSpaceManagedByLabelSelector() { Map managedByLabelDomains = new HashMap<>(); managedByLabelDomains.put(domainNamespaces[0], domainsUid[0]); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index 4a8849a0311..94afa42caa5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -306,7 +306,6 @@ WEBLOGIC_IMAGE_TO_USE_IN_SPEC, adminSecretName, createSecretsForImageRepos(domai @Test @Order(1) @DisplayName("Test to update data source url in the domain using auxiliary image") - @Tag("gate") void testUpdateDataSourceInDomainUsingAuxiliaryImage() { // create stage dir for auxiliary image diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java index 1dd9b556cda..bfae69cdbce 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java @@ -140,7 +140,6 @@ public void beforeEach() { @Test @Order(1) @DisplayName("Add a work manager to a model-in-image domain using dynamic update") - @Tag("gate") @Tag("crio") void testMiiAddWorkManager() { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java index 11dd73ae817..96c4d92a2da 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart2.java @@ -116,7 +116,6 @@ public void beforeEach() { @Test @DisplayName("Changing Weblogic datasource URL and deleting application with CommitUpdateAndRoll " + "using mii dynamic update") - @Tag("gate") void testMiiDeleteAppChangeDBUrlWithCommitUpdateAndRoll() { // This test uses the WebLogic domain created in BeforeAll method diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java index 16b17dfada7..4eac6a553c7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java @@ -110,6 +110,7 @@ @Tag("oke-weekly-sequential") @Tag("kind-sequential") @Tag("okd-wls-mrg") +@Tag("gate") class ItMonitoringExporterWebApp { // domain constants diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java index 77cda573f93..ee73e93ba8a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java @@ -79,6 +79,7 @@ @Tag("okd-wls-srg") @Tag("oke-arm") @Tag("oke-parallel") +@Tag("gate") class ItMultiDomainModels { // domain constants @@ -146,7 +147,6 @@ public static void initAll(@Namespaces(5) List namespaces) { @DisplayName("scale cluster by patching domain resource with four different type of domains and " + "verify admin server is accessible via REST interface.") @ValueSource(strings = {"modelInImage", "domainInImage", "domainOnPV", "auxiliaryImageDomain"}) - @Tag("gate") void testScaleClustersAndAdminRESTAccess(String domainType) { DomainResource domain = createDomainBasedOnDomainType(domainType); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 8fd03d580bb..5838899cc90 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -143,7 +143,6 @@ public void beforeEach() { @Order(1) @Test @DisplayName("Restart the configured cluster with serverStartPolicy") - @Tag("gate") void testConfigClusterRestart() { String configServerPodName = domainUid + "-config-cluster-server1"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 225c410e7e8..9717a359b56 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -100,6 +100,7 @@ @Tag("olcne-mrg") @Tag("oke-weekly-sequential") @Tag("kind-sequential") +@Tag("gate") class ItT3Channel { // namespace constants private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java index 4901587e8f3..4e5433ebe01 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsDomainOnPV.java @@ -117,7 +117,6 @@ public static void initAll(@Namespaces(2) List namespaces) { */ @Test @DisplayName("Create a WLS domain on PV using simplified feature, Operator creates PV/PVC and WLS Domain") - @Tag("gate") void testOperatorCreatesPvPvcWlsDomain() { String domainUid = "wlsonpv-simplified"; final String pvName = getUniqueName(domainUid + "-pv-"); From 66946eb150442a285b7a965b596f1e761ab46949 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 14 Oct 2024 18:07:18 -0400 Subject: [PATCH 192/356] Add recent OCNE certifications --- .../site/content/introduction/platforms/environments.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/site/content/introduction/platforms/environments.md b/documentation/site/content/introduction/platforms/environments.md index 7a25d99c809..9bf97f3438d 100644 --- a/documentation/site/content/introduction/platforms/environments.md +++ b/documentation/site/content/introduction/platforms/environments.md @@ -79,6 +79,8 @@ WebLogic Kubernetes Operator is certified for use on OKE with Kubernetes 1.25.0+ [Oracle Cloud Native Environment](https://docs.oracle.com/en/operating-systems/olcne/) is a fully integrated suite for the development and management of cloud-native applications. Based on Open Container Initiative (OCI) and Cloud Native Computing Foundation (CNCF) standards, Oracle Cloud Native Environment delivers a simplified framework for installations, updates, upgrades, and configuration of key features for orchestrating microservices. WebLogic Server and the WebLogic Kubernetes Operator are certified and supported on Oracle Cloud Native Environment (OCNE): +* OCNE 1.9 with Kubernetes 1.29.3+ +* OCNE 1.8 with Kubernetes 1.28.8+ * OCNE 1.7 with Kubernetes 1.26.6+ * OCNE 1.6 with Kubernetes 1.25.11+ From 8c314741f37152efe4fd23c669754465b5dab92e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 16 Oct 2024 09:59:59 -0400 Subject: [PATCH 193/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 635c40e0422..75a5eba8d30 100644 --- a/pom.xml +++ b/pom.xml @@ -688,12 +688,12 @@ 3.5.0 3.5.0 3.1.1 - 3.10.0 + 3.10.1 3.5.0 3.8.0 3.6.0 3.4.1 - 10.18.1 + 10.18.2 1.0 3.5.0 3.2.7 @@ -731,14 +731,14 @@ 4.0.2 6.1.0 0.16.0 - 2.17.2 - 2.17.2 + 2.18.0 + 2.18.0 2.3 2.11.0 10.0.4 2.0.16 1.5.8 - 3.25.5 + 4.28.2 2.4.11 9.41.2 ${project.basedir}/src-generated-swagger From cd25b066f1ad46237e265725ce4c313c9afc761e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 16 Oct 2024 13:11:31 -0400 Subject: [PATCH 194/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index bb70c4a7b58..66289585be0 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -45,7 +45,7 @@ org.ow2.asm asm - 9.7 + 9.7.1 test From 8ab83839806898e7cf64335c025c9edf94affdb0 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Wed, 16 Oct 2024 19:40:51 +0000 Subject: [PATCH 195/356] workaround OWLS-122679 - do not create WDT base image in InitializationTask in crio-pipeline and OCNE --- .../kubernetes/extensions/InitializationTasks.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 336b3daaab1..c91539ad84b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -240,7 +240,7 @@ public void beforeAll(ExtensionContext context) { logger.info("Build/Check wdt-basic image with tag {0}", WDT_BASIC_IMAGE_TAG); // build WDT basic image if does not exits - if (! imageExists(WDT_BASIC_IMAGE_NAME, WDT_BASIC_IMAGE_TAG)) { + if (!imageExists(WDT_BASIC_IMAGE_NAME, WDT_BASIC_IMAGE_TAG) && !CRIO && !OCNE) { logger.info("Building wdt-basic image {0}", wdtBasicImage); testUntil( withVeryLongRetryPolicy, @@ -249,7 +249,7 @@ public void beforeAll(ExtensionContext context) { logger, "createBasicImage to be successful"); } else { - logger.info("!!!! domain image {0} exists !!!!", wdtBasicImage); + logger.info("!!!! domain image {0} exists !!!! or env is not OCNE based", wdtBasicImage); } /* Check image exists using WLSIMG_BUILDER images | grep image tag. @@ -261,8 +261,10 @@ public void beforeAll(ExtensionContext context) { assertTrue(doesImageExist(MII_BASIC_IMAGE_TAG), String.format("Image %s doesn't exist", miiBasicImage)); - assertTrue(doesImageExist(WDT_BASIC_IMAGE_TAG), + if (!CRIO && !OCNE) { + assertTrue(doesImageExist(WDT_BASIC_IMAGE_TAG), String.format("Image %s doesn't exist", wdtBasicImage)); + } logger.info(WLSIMG_BUILDER + " login"); testUntil(withVeryLongRetryPolicy, @@ -280,7 +282,9 @@ public void beforeAll(ExtensionContext context) { // add images only if SKIP_BUILD_IMAGES_IF_EXISTS is not set if (!SKIP_BUILD_IMAGES_IF_EXISTS) { images.add(miiBasicImage); - images.add(wdtBasicImage); + if (!CRIO && !OCNE) { + images.add(wdtBasicImage); + } } for (String image : images) { From e3ad96da0fa95cbb5bd46d1b469ee7f4b3fbc63e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 16 Oct 2024 16:14:56 -0400 Subject: [PATCH 196/356] Prepare for version 4.2.9 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 14adeea840f..a7b75a1e926 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 4857f1f550d..ff8fddf27a1 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 0b5c8941eb7..afdf054ce2c 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index ac6a8320c80..b15d8612ed1 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.9-SNAPSHOT + 4.2.9 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 5edde087cc4..12cabccea3b 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 66289585be0..8f2086b6ef5 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.9-SNAPSHOT + 4.2.9 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 6251a4d0007..d13bccba049 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 75a5eba8d30..3212f3ac013 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index c2c13702735..5add91f79a9 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9-SNAPSHOT + 4.2.9 operator-swagger From e0fa5d74df58189ecaf03a4e740767bad4a855c9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 16 Oct 2024 17:06:39 -0400 Subject: [PATCH 197/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index a7b75a1e926..04ffb0a31dd 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index ff8fddf27a1..1a4593578b3 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index afdf054ce2c..62c00660f6b 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index b15d8612ed1..b78af0a9616 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.9 + 4.2.10-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 12cabccea3b..c496e41b99e 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 8f2086b6ef5..4dd6ab98e8a 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.9 + 4.2.10-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index d13bccba049..acd7ca46ef8 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 3212f3ac013..918ae1770c6 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 5add91f79a9..ad34a7438a6 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.9 + 4.2.10-SNAPSHOT operator-swagger From 7230727f47a838122e1502f2ff65792e15c35adb Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 17 Oct 2024 15:16:14 -0400 Subject: [PATCH 198/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 918ae1770c6..1f7f5742f88 100644 --- a/pom.xml +++ b/pom.xml @@ -685,7 +685,7 @@ 3.4.2 3.3.1 3.20.0 - 3.5.0 + 3.5.1 3.5.0 3.1.1 3.10.1 @@ -717,7 +717,7 @@ 4.2.2 19.0.1 3.0.1u2 - 2.0.20 + 2.0.21 4.12.0 3.9.1 1.78.1 @@ -726,7 +726,7 @@ 1.7.0 1.3.2 UTF-8 - 3.1.8 + 3.1.9 1.1.7 4.0.2 6.1.0 From b94d8414930fe0e0a54b3bef61d72dbc0959dc72 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Fri, 18 Oct 2024 13:39:09 +0000 Subject: [PATCH 199/356] Added OCI load balancer options to use flexible shape, updated k8s OKE to 1.30.1 --- Jenkinsfile.oke | 11 +- .../kubernetes/actions/impl/NginxParams.java | 1 - .../actions/impl/TraefikParams.java | 4 +- .../kubernetes/utils/LoadBalancerUtils.java | 90 ++++++++++- .../kubernetes/utils/MonitoringUtils.java | 18 +++ .../oke/terraform/okemodule/oke.create.sh | 140 ++++++++++-------- 6 files changed, 192 insertions(+), 72 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index c6c5104d764..44ca0113354 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -83,18 +83,19 @@ pipeline { string(name: 'OKE_KUBE_VERSION', description: 'kube version for oke cluster', - defaultValue: '1.29.1' + defaultValue: '1.30.0' ) string(name: 'IMAGE_ID', description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', //defaultValue OKE1.26.2: 'ocid1.image.oc1.phx.aaaaaaaaaizmtmozeudeeuq7o5ir7dkl2bkxbbb3tgomshqbqn6jpomrsjza' //1.27.2 oke defaultValue: 'ocid1.image.oc1.phx.aaaaaaaaypr5r5drojwytghw6e6mvpjsscrnkuwtmqlmvmix7kjb2zcnc7wa' - defaultValue: 'ocid1.image.oc1.phx.aaaaaaaa22u45gr3ikxnc6rius2qil2kz5k3e7p476c4usr6qnvql4l5dxea' + //1.29.1 'ocid1.image.oc1.phx.aaaaaaaa22u45gr3ikxnc6rius2qil2kz5k3e7p476c4usr6qnvql4l5dxea' + defaultValue: 'ocid1.image.oc1.phx.aaaaaaaahgrs3zcwrvutjtni557ttrt62uggseijsmqxacr7dym423uaokcq' ) string(name: 'KUBECTL_VERSION', description: 'kubectl version', - defaultValue: '1.26.2' + defaultValue: '1.30.0' ) string(name: 'HELM_VERSION', description: 'Helm version', @@ -144,7 +145,7 @@ pipeline { ) string(name: 'NODE_SHAPE', description: '', - defaultValue: "VM.Standard.E4.Flex" + defaultValue: "VM.Standard.E5.Flex" ) string(name: 'MOUNT_TARGET_OCID', description: 'only for debug runs on wko-oke-dev', @@ -273,6 +274,7 @@ pipeline { --auth=instance_principal chmod +x ${WORKSPACE}/bin/kubectl kubectl version --client=true + ''' } } @@ -321,6 +323,7 @@ pipeline { chmod 600 ${wkotest_ssh_pk} COMPARTMENT_OCID=${compartment_id} + export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True echo 'Set env vars needed for integration tests...' diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/NginxParams.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/NginxParams.java index aca077a6afd..9602c4710ef 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/NginxParams.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/NginxParams.java @@ -35,7 +35,6 @@ public class NginxParams { private static final String IP_FAMILY_POLICY = "controller.service.ipFamilyPolicy"; private static final String IP_FAMILIES = "controller.service.ipFamilies"; private static final String TYPE = "controller.service.type"; - // Adding some of the most commonly used params for now private int nodePortsHttp; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/TraefikParams.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/TraefikParams.java index 133181ca78b..c940bd7fd07 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/TraefikParams.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/TraefikParams.java @@ -33,7 +33,6 @@ public class TraefikParams { private static final String TRAEFIK_IMAGE_TAG = "image.tag"; private static final String INGRESS_CLASS_NAME = "ingressClass.name"; private static final String TYPE = "service.type"; - public TraefikParams() { ingressClassName = UniqueName.uniqueName("traefik-"); @@ -72,7 +71,7 @@ public TraefikParams traefikImageTag(String traefikImageTag) { this.traefikImageTag = traefikImageTag; return this; } - + public String getIngressClassName() { return ingressClassName; } @@ -106,7 +105,6 @@ public Map getValues() { values.put(TRAEFIK_IMAGE_TAG, traefikImageTag); values.put(INGRESS_CLASS_NAME, ingressClassName); values.put(TYPE, type); - values.values().removeIf(Objects::isNull); return values; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index fecb68960f4..7d5a5037ffe 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -35,6 +35,7 @@ import oracle.weblogic.kubernetes.actions.impl.primitive.HelmParams; import oracle.weblogic.kubernetes.extensions.InitializationTasks; import oracle.weblogic.kubernetes.logging.LoggingFacade; +import org.jetbrains.annotations.Nullable; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.COMPARTMENT_OCID; @@ -385,13 +386,15 @@ public static String getLoadBalancerIP(String namespace, String lbName, boolean return null; } - private static boolean checkLoadBalancerHealthy(String namespace, String lbServiceName) { + private static boolean checkLoadBalancerHealthy(String namespace, String lbServiceName) { + String lbPublicIP = assertDoesNotThrow(() -> getLoadBalancerIP(namespace, lbServiceName)); InitializationTasks.registerLoadBalancerExternalIP(lbPublicIP); LoggingFacade logger = getLogger(); String testcompartmentid = System.getProperty("wko.it.oci.compartment.ocid"); logger.info("wko.it.oci.compartment.ocid property " + testcompartmentid); + final String command = "oci lb load-balancer list --compartment-id " + testcompartmentid + " --query \"data[?contains(\\\"ip-addresses\\\"[0].\\\"ip-address\\\", '" + lbPublicIP + "')].id | [0]\" --raw-output --all"; @@ -409,13 +412,44 @@ private static boolean checkLoadBalancerHealthy(String namespace, String lbServi // Clean up the string to extract the Load Balancer ID String lbOCID = result.stdout().trim(); + boolean isFlexible = isLoadBalancerShapeFlexible(lbOCID); + + if (!isFlexible) { + logger.info("Updating load balancer shape to flexible"); + + final String command2 = "oci lb load-balancer update-load-balancer-shape --load-balancer-id " + + lbOCID + " --shape-name flexible --shape-details" + + " '{\"minimumBandwidthInMbps\": 10, \"maximumBandwidthInMbps\": 400}' --force"; + + result = assertDoesNotThrow(() -> exec(command2, true)); + logger.info("Command: {}, Exit value: {}, Stdout: {}, Stderr: {}", + command2, result.exitValue(), result.stdout(), result.stderr()); + + if (result == null || result.stdout() == null) { + return false; + } else if (result.exitValue() != 0 && !result.stdout().contains("is currently being modified")) { + return false; + } + + testUntil( + assertDoesNotThrow(() -> checkWorkRequestUpdateShapeSucceeded( + lbOCID), "isOCILoadBalancer work request to update shape is not ready"), + logger, + "load balancer shape is updating "); + testUntil( + assertDoesNotThrow(() -> checkLoadBalancerShapeFlexible( + lbOCID), "checkLoadBalancerShape is not flexible "), + logger, + "load balancer shape can't be checked, retrying "); + } + //check health status final String command1 = "oci lb load-balancer-health get --load-balancer-id " + lbOCID; logger.info("Command to retrieve Load Balancer health status is: {0} ", command1); result = assertDoesNotThrow(() -> exec(command1, true)); logger.info("The command returned exit value: " + result.exitValue() + " command output: " + result.stderr() + "\n" + result.stdout()); - + logger.info("result.stderr: \n{0}", result.stderr()); if (result == null || result.exitValue() != 0 || result.stdout() == null) { return false; } @@ -424,6 +458,58 @@ private static boolean checkLoadBalancerHealthy(String namespace, String lbServi } + @Nullable + private static boolean isLoadBalancerShapeFlexible(String lbOCID) { + LoggingFacade logger = getLogger(); + + final String checkShapeCommand = "oci lb load-balancer get --load-balancer-id " + + lbOCID + " | jq '.data[\"shape-name\"], .data[\"shape-details\"]'"; + ExecResult result = assertDoesNotThrow(() -> exec(checkShapeCommand, true)); + logger.info("The command " + checkShapeCommand + " returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + logger.info("result.stderr: \n{0}", result.stderr()); + if (result == null || result.exitValue() != 0 || result.stdout() == null || !result.stdout().contains("flexible")) { + return false; + } + return true; + } + + private static Callable checkLoadBalancerShapeFlexible(String loadBalancerOCID) { + return () -> isLoadBalancerShapeFlexible(loadBalancerOCID); + } + + /** + * Check work request status for load balancer. + * @param loadBalancerOCID - load balancer OCID + * @return true if succeeded , false over vise. + */ + public static boolean isWorkRequestUpdateShapeSucceeded(String loadBalancerOCID) { + + LoggingFacade logger = getLogger(); + final String command = "oci lb work-request list --load-balancer-id " + + loadBalancerOCID + + " --query 'data[?type == `UpdateShape`].{id:id, lifecycleState:\"lifecycle-state\", " + + "message:message, timeFinished:\"time-finished\"}' " + + "| jq '.[] | select(.lifecycleState == \"SUCCEEDED\")'"; + ExecResult result = assertDoesNotThrow(() -> exec(command, true)); + logger.info("The command " + command + " returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + logger.info("result.stderr: \n{0}", result.stderr()); + if (result == null || result.exitValue() != 0 || result.stdout() == null || result.stderr().contains("ERROR")) { + return false; + } + return true; + } + + /** + * Check if lb work request status is succeeded. + * + * @param loadBalancerOCID lb ocid + * @return true if succeeded, false otherwise + */ + public static Callable checkWorkRequestUpdateShapeSucceeded(String loadBalancerOCID) { + return () -> isWorkRequestUpdateShapeSucceeded(loadBalancerOCID); + } /** Upgrade Traefik and wait for up to five minutes for the Traefik pod to be ready. * diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index 80a6c6e6856..f7851aabf85 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -105,6 +105,7 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.isPrometheusAdapterReady; import static oracle.weblogic.kubernetes.assertions.TestAssertions.isPrometheusReady; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndCheckForServerNameInResponse; +import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReady; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.addSccToDBSvcAccount; @@ -121,6 +122,7 @@ import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getPodName; import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; @@ -261,6 +263,22 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa logger.info("Executing Curl cmd {0}", curlCmd); logger.info("Checking searchKey: {0}", searchKey); logger.info(" expected Value {0} ", expectedVal); + if (OKE_CLUSTER) { + try { + if (!callWebAppAndWaitTillReady(curlCmd, 5)) { + ExecResult result = ExecCommand.exec(KUBERNETES_CLI + " get all -A"); + logger.info(result.stdout()); + //restart core-dns service + result = ExecCommand.exec(KUBERNETES_CLI + " rollout restart deployment coredns -n kube-system"); + logger.info(result.stdout()); + checkPodReady("coredns", null, "kube-system"); + result = ExecCommand.exec(curlCmd); + logger.info(result.stdout()); + } + } catch (Exception ex) { + logger.warning(ex.getLocalizedMessage()); + } + } testUntil( searchForKey(curlCmd, expectedVal), logger, diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh index dffa9220f3c..0b9bf87c766 100755 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh @@ -80,75 +80,91 @@ createRoleBindings () { ${KUBERNETES_CLI:-kubectl} config set-credentials $okeclustername-sa --token=$TOKEN ${KUBERNETES_CLI:-kubectl} config set-context --current --user=$okeclustername-sa } +checkKubernetesCliConnection() { + echo "Confirming ${KUBERNETES_CLI:-kubectl} can connect to the server..." -checkClusterRunning () { + # Get the cluster public IP + clusterPublicIP=$(oci ce cluster list --compartment-id="${compartment_ocid}" | jq -r '.data[] | select(."name" == "'"${okeclustername}"'" and (."lifecycle-state" == "ACTIVE")) | ."endpoints" | ."public-endpoint" | split(":")[0]') + + # Check if clusterPublicIP is empty or not + if [ -z "$clusterPublicIP" ]; then + echo "[ERROR] No active cluster found with name ${okeclustername}." + exit 1 + fi + echo "clusterPublicIP: ###$clusterPublicIP###" + unset NO_PROXY + export NO_PROXY=localhost,127.0.0.1,10.244.0.0/16,10.101.0.0/16,10.196.0.0/16,$clusterPublicIP + echo "export NO_PROXY=:$NO_PROXY" + + local myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) - echo "Confirm we have ${KUBERNETES_CLI:-kubectl} working..." - privateIP=${vcn_cidr_prefix} - myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then echo "[ERROR] Unable to connect to the server: net/http: TLS handshake timeout" - echo '- could not talk to OKE cluster, aborting' - cd ${terraformVarDir} - terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars - terraform apply -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars - echo "retrying to execute KUBERNETES_CLI" - clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') - echo "clusterIp : $clusterIP" - clusterPublicIP=${clusterIP:1:-6} - echo " clusterPublicIP : ${clusterPublicIP}" - echo "NO_PROXY before : ${NO_PROXY}" - export NO_PROXY=${clusterPublicIP} - echo "NO_PROXY:" $NO_PROXY + unset http_proxy + unset https_proxy myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then - echo "[ERROR] Unable to connect to the server: net/http: TLS handshake timeout" - echo '- could not talk to OKE cluster, aborting' - cd ${terraformVarDir} - terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars - exit 1 + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + exit 1 fi fi - declare -a myline - myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) - NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` - status=$myline[0] - max=100 - count=1 - - for i in {0..1} - do - while [ "${myline[i]}" != "Ready" -a $count -le $max ] ; do - echo "echo '[ERROR] Some Nodes in the Cluster are not in the Ready Status , sleep 10s more ..." - sleep 10 - myline=(`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}'`) - NODE_IP=`${KUBERNETES_CLI:-kubectl} get nodes -o wide| grep "${privateIP}" | awk '{print $7}'` - echo "myline[i] ${myline[i]}" - [[ ${myline[i]} -eq "Ready" ]] - echo "Status is ${myline[i]} Iter [$count/$max]" - count=`expr $count + 1` - done + if echo "$myline_output" | grep -q "couldn't get current server API group"; then + echo "[ERROR] Unable to connect to the server: couldn't get current server API group, connection refused" + echo '- check errors during OKE cluster creation' + echo '- could not talk to OKE cluster, aborting' + + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + exit 1 + fi + +} + +checkClusterRunning() { + checkKubernetesCliConnection + + local privateIP=${vcn_cidr_prefix} + declare -a myline=($(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}')) + local NODE_IP=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $7}') + + local status=${myline[0]} + local max=100 + local count=1 + + for i in {0..1}; do + while [ "${myline[i]}" != "Ready" ] && [ $count -le $max ]; do + echo "[ERROR] Some Nodes in the Cluster are not in the Ready Status, sleeping for 10s..." + sleep 10 + myline=($(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $2}')) + NODE_IP=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | awk '{print $7}') + echo "myline[i]: ${myline[i]}" + echo "Status is ${myline[i]} Iter [$count/$max]" + count=$((count + 1)) + done done - NODES=`${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | wc -l` - if [ "$NODES" == "2" ]; then - echo '- looks good' + local NODES=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${privateIP}" | wc -l) + + if [ "$NODES" -eq 2 ]; then + echo '- looks good' else - echo '- could not talk to OKE cluster, aborting' - cd ${terraformVarDir} - terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars - exit 1 + echo '- could not talk to OKE cluster, aborting' + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + exit 1 fi - if [ $count -gt $max ] ; then - echo "[ERROR] Unable to start the nodes in oke cluster after 200s "; - cd ${terraformVarDir} - terraform destroy -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars - exit 1 + if [ $count -gt $max ]; then + echo "[ERROR] Unable to start the nodes in the OKE cluster after 200s" + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + exit 1 fi } + #MAIN propsFile=${1:-$PWD/oci.props} terraformVarDir=${2:-$PWD} @@ -199,18 +215,18 @@ createCluster export KUBECONFIG=${terraformVarDir}/${okeclustername}_kubeconfig -export okeclustername=\"${okeclustername}\" +#export okeclustername=\"${okeclustername}\" -echo " oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"'" +#echo " oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"'" -clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') -echo "clusterIp : $clusterIP" -clusterPublicIP=${clusterIP:1:-6} -echo " clusterPublicIP : ${clusterPublicIP}" -echo "NO_PROXY before : ${NO_PROXY}" -export NO_PROXY=${clusterPublicIP} -echo "NO_PROXY:" $NO_PROXY +#clusterIP=$(oci ce cluster list --compartment-id=${compartment_ocid} | jq '.data[] | select(."name" == '"${okeclustername}"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') +#echo "clusterIp : $clusterIP" +#clusterPublicIP=${clusterIP:1:-6} +#echo " clusterPublicIP : ${clusterPublicIP}" +#echo "NO_PROXY before : ${NO_PROXY}" +#export NO_PROXY=${clusterPublicIP} +#echo "NO_PROXY:" $NO_PROXY checkClusterRunning -echo "$okeclustername is up and running}" +echo "${okeclustername} is up and running" From 80b931301a2222d21a4f44266d154fa2933e6365 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 18 Oct 2024 09:47:35 -0400 Subject: [PATCH 200/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1f7f5742f88..6b845920e61 100644 --- a/pom.xml +++ b/pom.xml @@ -737,9 +737,9 @@ 2.11.0 10.0.4 2.0.16 - 1.5.8 + 1.5.11 4.28.2 - 2.4.11 + 2.5.1 9.41.2 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java From 364f175ac84db6186dffc3926dfb01a5541e1e45 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 18 Oct 2024 10:22:44 -0400 Subject: [PATCH 201/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b845920e61..b1f540dbd73 100644 --- a/pom.xml +++ b/pom.xml @@ -721,7 +721,7 @@ 4.12.0 3.9.1 1.78.1 - 5.11.1 + 5.11.2 5.7.1 1.7.0 1.3.2 From af3587fd9a8c77694d366f140a66c9b92c806134 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Fri, 18 Oct 2024 20:17:03 +0000 Subject: [PATCH 202/356] OWLS-122761, updated Jenkinsfile.armoke file to reflect all changes in terraform and version for OKE cluster --- Jenkinsfile.armoke | 122 ++++++++++++++---- Jenkinsfile.oke | 2 +- .../ItIstioCrossDomainTransaction.java | 1 - .../impl/primitive/WebLogicImageTool.java | 5 + .../extensions/InitializationTasks.java | 2 +- .../oke/terraform/okemodule/oke.create.sh | 4 +- .../oke/terraform/okemodule/template.tfvars | 2 +- 7 files changed, 106 insertions(+), 32 deletions(-) diff --git a/Jenkinsfile.armoke b/Jenkinsfile.armoke index 1bc2b33a406..3856f2c6ebe 100644 --- a/Jenkinsfile.armoke +++ b/Jenkinsfile.armoke @@ -2,13 +2,23 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // +CRON_SETTINGS_MAIN = '''H 3 * * 0-4 % MAVEN_PROFILE_NAME=oke-arm;CLUSTER_NAME=okearm;PARALLEL_RUN=false''' + +CRON_SETTINGS_42 = '''H 5 * * 6 % MAVEN_PROFILE_NAME=oke-arm;CLUSTER_NAME=seq42armwls12;PARALLEL_RUN=false + H 21 * * 5 % MAVEN_PROFILE_NAME=oke-arm;CLUSTER_NAME=seq42armwls14;PARALLEL_RUN=false;WEBLOGIC_IMAGE_TAG=14.1.1.0-generic-jdk8-ol8-241009.121334-arm64''' + +CRON_SETTINGS = "${env.JOB_NAME == 'wko-oke-arm' ? CRON_SETTINGS_42 : CRON_SETTINGS_MAIN}" pipeline { - agent { label 'large-arm' } + agent { label 'large-arm-iad' } options { timeout(time: 1800, unit: 'MINUTES') - disableConcurrentBuilds() } + triggers { + // timer trigger for "nightly build" + parameterizedCron(env.JOB_NAME == 'wko-oke-dev' ? + '' : CRON_SETTINGS) + } tools { maven 'maven-3.8.7' jdk 'jdk21-arm' @@ -19,6 +29,9 @@ pipeline { wko_tenancy = "${env.WKT_TENANCY}" wko_images_rep = '${wko_tenancy}/wkt/infra' wko_region = "${env.WKT_REGION}" + + wko_region_bucket = "us-phoenix-1" + compartment_id = "${env.WKT_TEST_COMPARTMENT_ID}" ocir_creds = 'wkt-ocir-creds' wko_files_bucket_token = 'wko-system-test-files-bucket-par-token' @@ -50,11 +63,11 @@ pipeline { start_time = sh(script: 'date +"%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" - CLUSTER_NAME = "${env.JOB_NAME == 'wko-oke-nightly-parallel' ? 'parcluster' : 'armcluster'}" kubeconfig_file = "${WORKSPACE}/terraform/${CLUSTER_NAME}_kubeconfig" MAVEN_PROFILE_NAME = "oke-arm" availability_domain = "mFEn:PHX-AD-1" PARALLEL_RUN = "false" + oke_run = "okemodule" } parameters { @@ -66,14 +79,18 @@ pipeline { string(name: 'OKE_KUBE_VERSION', description: 'kube version for oke cluster', - defaultValue: '1.27.2' + defaultValue: '1.29.1' ) string(name: 'IMAGE_ID', description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', - //defaultValue OKE1.26.2: 'ocid1.image.oc1.phx.aaaaaaaaaizmtmozeudeeuq7o5ir7dkl2bkxbbb3tgomshqbqn6jpomrsjza' - defaultValue: 'ocid1.image.oc1.phx.aaaaaaaa77h3zkjcfjoaou4aa3cz63fswtcglleuslt6d6zbqwacng43nqiq' + //defaultValue OKE1.30.1: 'ocid1.image.oc1.phx.aaaaaaaa5celqitaplckhgfnxois4fnxohzkgy4igrfsd5rtkwu4qkyhkzia' + defaultValue: 'ocid1.image.oc1.phx.aaaaaaaap5s265eoou3d65elvd22ihdi3dnrm2hn6bbewjjsluvw5jc32zia' ) + string(name: 'CLUSTER_NAME', + description: 'OKE cluster name', + defaultValue: "seqarmone" + ) string(name: 'KUBECTL_VERSION', description: 'kubectl version', defaultValue: '1.26.2' @@ -129,11 +146,12 @@ pipeline { ) string(name: 'WEBLOGIC_IMAGE_NAME', description: 'WebLogic base image name. Default is the image name in OCIR. Use middleware/weblogic for OCR.', - defaultValue: 'test-images/weblogic_cpu' + defaultValue: 'weblogic_arm64' ) string(name: 'WEBLOGIC_IMAGE_TAG', - description: '12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', - defaultValue: '12.2.1.4-generic-jdk8-ol7' + description: '12.2.1.4, 12.2.1.4-generic-jdk8-ol7, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '12.2.1.4-generic-jdk8-ol8-241008.161513-arm64' + //defaultValue: '14.1.1.0-generic-jdk8-ol8-241009.121334-arm64' ) string(name: 'FMWINFRA_IMAGE_NAME', description: 'FWM Infra image name. Default is the image name in OCIR. Use middleware/fmw-infrastructure for OCR.', @@ -151,6 +169,10 @@ pipeline { description: 'Oracle DB image tag', defaultValue: '19.19.0.0' ) + string(name: 'MOUNT_TARGET_OCID', + description: 'one mount target for all runs', + defaultValue: "ocid1.mounttarget.oc1.phx.aaaaaby27vhqpci5obuhqllqojxwiotqnb4c2ylefuzqaaaa" + ) string(name: 'MONITORING_EXPORTER_BRANCH', description: '', defaultValue: 'main' @@ -167,6 +189,10 @@ pipeline { description: '', defaultValue: '6.44.11' ) + string(name: 'REMOTECONSOLE_VERSION', + description: 'RemoteConsole version.', + defaultValue: '2.4.7' + ) booleanParam(name: 'COLLECT_LOGS_ON_SUCCESS', description: 'Collect logs for successful runs. Default is false.', defaultValue: false @@ -228,10 +254,12 @@ pipeline { steps { sh ''' export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ --name=helm/helm-v${HELM_VERSION}-linux-arm.tar.gz --file=helm.tar.gz \ - --auth=instance_principal + --auth=instance_principal --region=us-phoenix-1 tar zxf helm.tar.gz + mv linux-arm/helm ${WORKSPACE}/bin/helm rm -rf linux-arm helm version @@ -246,9 +274,10 @@ pipeline { steps { sh ''' export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ - --name=kubectl/kubectl-v${KUBECTL_VERSION}-arm --file=${WORKSPACE}/bin/kubectl \ - --auth=instance_principal + --name=kubectl/kubectl-v${KUBECTL_VERSION}-arm --file=${WORKSPACE}/bin/kubectl \ + --auth=instance_principal --region=us-phoenix-1 chmod +x ${WORKSPACE}/bin/kubectl ls -al ${WORKSPACE}/bin/kubectl kubectl version --client=true @@ -291,6 +320,8 @@ pipeline { private_subnet_ocid=$(oci network subnet list --compartment-id=${wkt_compartment_ocid} | jq '.data[] | select(."display-name" == "Private-Subnet-wktiso1")' | jq -r ."id") export WKT_TENANCY_OCID=${TENANCY_OCID} export WKT_USER_OCID=${USER_OCID} + + rm -rf ${WORKSPACE}/terraform mkdir -p ${WORKSPACE}/terraform mkdir -p ${WORKSPACE}/oci cat ${jenkins_home_directory}/.oci/config @@ -319,6 +350,7 @@ pipeline { #export IMAGE_TAG_WEBLOGIC="12.2.1.4" #export IMAGE_TAG_FMWINFRA="12.2.1.4" ssh_pubkey=`cat ${wkotest_ssh_pubcert}` + ssh_pk=`cat ${wkotest_ssh_pk}` ################# echo "Generating property file oci.prop for terraform scripts" @@ -338,19 +370,22 @@ ocipk.path=${jenkins_home_directory}/.oci/oci-signing-key.pem vcn.cidr.prefix=${VCN_CIDR_PREFIX} vcn.cidr=${VCN_CIDR_PREFIX}.1.0/24 vcn.ocid=${vcn_ocid} +mounttarget.ocid=${MOUNT_TARGET_OCID} pub.subnet.ocid=${pub_subnet_ocid} private.subnet.ocid=${private_subnet_ocid} nodepool.shape=${NODE_SHAPE} nodepool.imagename=${IMAGE_ID} k8s.version=v${OKE_KUBE_VERSION} nodepool.ssh.pubkey=${ssh_pubkey} +nodepool.ssh.pk=${ssh_pk} +nodepool.ssh.pubkeypath=${wkotest_ssh_pubcert} +nodepool.ssh.pkpath=${wkotest_ssh_pk} terraform.installdir=${WORKSPACE}/terraform/terraforminstall EOF ################## echo "prop files " cat $OCI_PROP_FILE - cp -rf ${WORKSPACE}/kubernetes/samples/scripts/terraform/template.tfvars ${WORKSPACE}/terraform/. mkdir -p ${WORKSPACE}/terraform/terraforminstall ''' @@ -383,7 +418,8 @@ EOF OCI_PROP_FILE="${WORKSPACE}/terraform/oci.prop" CLUSTER_NAME="${CLUSTER_NAME}" KUBECONFIG ="${WORKSPACE}/terraform/${CLUSTER_NAME}_kubeconfig" - terraform_script_dir_name = "${WORKSPACE}/integration-tests/src/test/resources/oke/terraform/okeint" + terraform_script_dir_name = "${WORKSPACE}/integration-tests/src/test/resources/oke/terraform/${oke_run}" + } steps { @@ -397,14 +433,16 @@ EOF echo 'Create a OKE cluster ${CLUSTER_NAME}' - cp -rf ${terraform_script_dir_name}/*.* ${WORKSPACE}/terraform/. + cp -rf ${terraform_script_dir_name}/* ${WORKSPACE}/terraform/. chmod 777 ${WORKSPACE}/terraform/*.sh mkdir -p ${WORKSPACE}/terraform/terraforminstall sh ${WORKSPACE}/terraform/oke.create.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform arm - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"$CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + + #clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"$CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == "'"${CLUSTER_NAME}"'" and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" @@ -444,9 +482,12 @@ EOF compartment_ocid=${compartment_id} echo "creating storage class to setup OFSS ..." echo "getting MountTarget ID" - mount_target_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | .id'` + mount_target_id=${MOUNT_TARGET_OCID} + #mount_target_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | .id'` + + #clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == "'"${CLUSTER_NAME}"'" and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" @@ -469,12 +510,13 @@ EOF environment { runtime_path = "${WORKSPACE}/bin:${PATH}" clusterName = "${CLUSTER_NAME}" - FSS_DIR = "/oketest1,/oketest2,/oketest3,/oketest4,/oketest5,/oketest6,/oketest7,/oketest8,/oketest9,/oketest10,/oketest11,/oketest12,/oketest13,/oketest14,/oketest15" + FSS_DIR = "${env.JOB_NAME == 'wko-oke-nightly' ? '/${clusterName}oketest1,/${clusterName}oketest2' : '/${clusterName}oketest1,/${clusterName}oketest2'}" } steps { script { def res = 0 + currentBuild.description = "${GIT_BRANCH} ${MAVEN_PROFILE_NAME} ${CLUSTER_NAME}" res = sh(script: ''' if [ -z "${IT_TEST}" ] && [ "${MAVEN_PROFILE_NAME}" = "integration-tests" ]; then echo 'ERROR: All tests cannot be run with integration-tests profile' @@ -491,10 +533,10 @@ EOF ]) { sh ''' if [ -z $WIT_DOWNLOAD_URL ]; then - WIT_DOWNLOAD_URL="https://objectstorage.${wko_region}.oraclecloud.com/p/${WKO_BUCKET_TOKEN}/n/${wko_tenancy}/b/wko-system-test-files/o/imagetool-main.zip" + WIT_DOWNLOAD_URL="https://objectstorage.${wko_region_bucket}.oraclecloud.com/p/${WKO_BUCKET_TOKEN}/n/${wko_tenancy}/b/wko-system-test-files/o/imagetool-main.zip" fi if [ -z $WDT_DOWNLOAD_URL ]; then - WDT_DOWNLOAD_URL="https://objectstorage.${wko_region}.oraclecloud.com/p/${WKO_BUCKET_TOKEN}/n/${wko_tenancy}/b/wko-system-test-files/o/weblogic-deploy-main.zip" + WDT_DOWNLOAD_URL="https://objectstorage.${wko_region_bucket}.oraclecloud.com/p/${WKO_BUCKET_TOKEN}/n/${wko_tenancy}/b/wko-system-test-files/o/weblogic-deploy-main.zip" fi export PATH=${runtime_path} @@ -504,8 +546,11 @@ EOF mkdir -m777 -p "${WORKSPACE}/.mvn" touch ${WORKSPACE}/.mvn/maven.config export KUBECONFIG=${kubeconfig_file} + export COMPARTMENT_OCID=${compartment_id} + echo "COMPARTMENT_OCID : ${COMPARTMENT_OCID}" + + clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == "'"${CLUSTER_NAME}"'" and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') - clusterIP=$(oci ce cluster list --compartment-id=${compartment_id} | jq '.data[] | select(."name" == '\"${CLUSTER_NAME}\"' and (."lifecycle-state" == "ACTIVE"))' | jq ' ."endpoints" | ."public-endpoint"') echo "clusterIp : $clusterIP" clusterPublicIP=${clusterIP:1:-6} echo " clusterPublicIP : ${clusterPublicIP}" @@ -523,17 +568,24 @@ EOF DOCKER_CLI_EXPERIMENTAL=enabled export DOCKER_BUILD_KIT=1 export DOCKER_CLI_EXPERIMENTAL=enabled + export AVAILABILITY_DOMAIN=${availability_domain} NODE_IP=`kubectl get nodes -o wide| awk '{print $7}'| head -n2 | tail -n1` echo "second node external IP ${NODE_IP}" export NODE_IP=${NODE_IP} - mt_privateip_id=`oci fs mount-target list --compartment-id=${compartment_ocid} --display-name=${clusterName}-mt --availability-domain=${availability_domain} | jq -r '.data[] | ."private-ip-ids"[]'` + mt_privateip_id=`oci fs mount-target get --mount-target-id=${MOUNT_TARGET_OCID} | jq -r '.data| ."private-ip-ids"[]'` - mt_private_ip=`oci network private-ip get --private-ip-id "${mt_privateip_id}" | jq -r '.data | ."ip-address"'` + # Check if the mt_privateip_id is an array + if [ "$(declare -p mt_privateip_id 2>/dev/null | grep -o 'declare -a')" == "declare -a" ]; then + # Select first + mt_private_ip=`oci network private-ip get --private-ip-id "${mt_privateip_id[0]}" | jq -r '.data | ."ip-address"'` + else + mt_private_ip=`oci network private-ip get --private-ip-id "${mt_privateip_id}" | jq -r '.data | ."ip-address"'` + fi if [ -z "${mt_private_ip}" ]; then echo "Mount Target was not setup properly , clean up Kubernetes cluster" - sh ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform + sh ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform ${AVAILABILITY_DOMAIN} exit -1 fi export NFS_SERVER=$mt_private_ip @@ -571,6 +623,9 @@ EOF echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.grafana.chart.version=\"${GRAFANA_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.collect.logs.on.success=\"${COLLECT_LOGS_ON_SUCCESS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.oci.compartment.ocid=\"${COMPARTMENT_OCID}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Djdk.httpclient.allowRestrictedHeaders=\"host\"" >> ${WORKSPACE}/.mvn/maven.config echo "${WORKSPACE}/.mvn/maven.config contents:" cat "${WORKSPACE}/.mvn/maven.config" @@ -596,6 +651,8 @@ EOF export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" export DOCKER_BUILD_KIT=1 export DOCKER_CLI_EXPERIMENTAL=enabled + export no_proxy="${NO_PROXY},localhost,127.0.0.1,.us.oracle.com,.oraclecorp.com,login.oracle.com" + export NO_PROXY="${no_proxy}" if ! mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/oketest.log"; then echo "integration-tests failed" @@ -611,11 +668,12 @@ EOF export PATH="${WORKSPACE}/bin:${PATH}" export KUBECONFIG=${kubeconfig_file} export OCI_CLI_CONFIG_FILE=${WORKSPACE}/oci/config + export AVAILABILITY_DOMAIN=${availability_domain} export OCI_CLI_PROFILE=${oci_profile} mkdir -m777 -p ${result_root}/kubelogs mkdir -m777 -p "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" sudo mv -f ${result_root}/* "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" - ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform + ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform ${AVAILABILITY_DOMAIN} ''' @@ -631,18 +689,28 @@ EOF sh ''' export PATH="${WORKSPACE}/bin:${PATH}" export KUBECONFIG=${kubeconfig_file} + export AVAILABILITY_DOMAIN=${availability_domain} echo 'Remove old OKE cluster (if any)...' if [ -f "$OCI_PROP_FILE" ] && [ -f "${WORKSPACE}/terraform/oke.delete.sh" ]; then - ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform + ${WORKSPACE}/terraform/oke.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform ${AVAILABILITY_DOMAIN} + fi + + if [ "${MAVEN_PROFILE_NAME}" = "oke-sequential" ] && [ "${BRANCH}" = "release/4.2" ]; then + compname="wkt" + wkt_compartment_ocid=$(oci iam compartment list --compartment-id-in-subtree true --all | jq --arg compname "$compname" '.data[] | select(."name"==$compname)' | jq -r ."id") + sec_list_id=$(oci network security-list list --compartment-id="$wkt_compartment_ocid" --display-name=Security-List-wktiso1 | jq -r '.data[] | ."id"') + oci network security-list update --security-list-id="$sec_list_id" --ingress-security-rules='[{"description": "east west","icmp-options": null,"is-stateless": false,"protocol": "all","source": "10.196.0.0/16","source-type": "CIDR_BLOCK","tcp-options": null,"udp-options": null},{"description": null,"icmp-options": {"code": null,"type": 3},"is-stateless": false,"protocol": "1", "source": "10.196.0.0/16", "source-type": "CIDR_BLOCK","tcp-options": null, "udp-options": null}, {"description": null, "icmp-options": {"code": 4, "type": 3},"is-stateless": false, "protocol": "1", "source": "0.0.0.0/0","source-type": "CIDR_BLOCK", "tcp-options": null, "udp-options": null}, {"description": null, "icmp-options": null, "is-stateless": false, "protocol": "6","source": "0.0.0.0/0", "source-type": "CIDR_BLOCK", "tcp-options": null, "udp-options": null}, {"description": null, "icmp-options": null, "is-stateless": false,"protocol": "6", "source": "0.0.0.0/0", "source-type": "CIDR_BLOCK", "tcp-options": {"destination-port-range": {"max": 22, "min": 22}, "source-port-range": null}, "udp-options": null}]' --egress-security-rules='[{"description": null, "destination": "0.0.0.0/0", "destinationType": "CIDR_BLOCK", "icmpOptions": null, "isStateless": false, "protocol": "all", "tcpOptions": null, "udpOptions": null}, {"description": null, "destination": "oci-phx-objectstorage", "destinationType": "SERVICE_CIDR_BLOCK", "icmpOptions": null, "isStateless": false, "protocol": "6", "tcpOptions": null, "udpOptions": null}]' --force fi rm -f ${jenkins_home_directory}/.oci/config rm -f ${jenkins_home_directory}/.oci/oci-signing-key.pem rm -f ${WORKSPACE}/.ssh/* rm -rf ${WORKSPACE}/.mvn + rm -rf ${WORKSPACE}/* ''' + } } } diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 44ca0113354..bd60aa39bf4 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -83,7 +83,7 @@ pipeline { string(name: 'OKE_KUBE_VERSION', description: 'kube version for oke cluster', - defaultValue: '1.30.0' + defaultValue: '1.30.1' ) string(name: 'IMAGE_ID', description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java index 4db9d327090..cf90c4607b9 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioCrossDomainTransaction.java @@ -88,7 +88,6 @@ @DisplayName("Verify cross domain transaction with istio enabled is successful") @IntegrationTest @Tag("kind-parallel") -@Tag("oke-arm") @Tag("oke-parallel") class ItIstioCrossDomainTransaction { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java index f55206814c5..478846c6c79 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java @@ -9,6 +9,7 @@ import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.ExecResult; +import static oracle.weblogic.kubernetes.TestConstants.ARM; import static oracle.weblogic.kubernetes.TestConstants.BUSYBOX_IMAGE; import static oracle.weblogic.kubernetes.TestConstants.BUSYBOX_TAG; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_NAME; @@ -185,6 +186,10 @@ private String buildWitCommand() { + " --wdtJavaOptions " + YAML_MAX_FILE_SIZE_PROPERTY + ownership; + if (ARM) { + command += " --platform linux/arm64"; + } + if (params.wdtModelOnly()) { command += " --wdtModelOnly "; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index c91539ad84b..1346ec76da0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -319,7 +319,7 @@ public void beforeAll(ExtensionContext context) { installTraefikLB(); } //install Oracle Database operator as a one time task - if (!OCNE && !OKD && !CRIO) { + if (!OCNE && !OKD && !CRIO && !ARM) { installOracleDBOperator(); } diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh index 0b9bf87c766..f1989bfaed8 100755 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh @@ -30,8 +30,9 @@ generateTFVarFile() { sed -i -e "s:@NODEPOOLIMAGENAME@:${nodepool_imagename}:g" ${tfVarsFiletfVarsFile} sed -i -e "s:@NODEPOOLSSHPUBKEY@:${nodepool_ssh_pubkeypath}:g" ${tfVarsFiletfVarsFile} sed -i -e "s:@NODEPOOLSSHPK@:${nodepool_ssh_pkpath}:g" ${tfVarsFiletfVarsFile} - sed -i -e "s:@REGION@:${region}:g" ${tfVarsFiletfVarsFile} sed -i -e "s:@MOUNTTARGETOCID@:${mount_target_ocid}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@REGION@:${region}:g" ${tfVarsFiletfVarsFile} + sed -i -e "s:@REGIONSHORT@:${region_short}:g" ${tfVarsFiletfVarsFile} echo "Generated TFVars file [${tfVarsFiletfVarsFile}]" } @@ -193,6 +194,7 @@ nodepool_ssh_pkpath=$(prop 'nodepool.ssh.pkpath') region=$(prop 'region') terraformDir=$(prop 'terraform.installdir') mount_target_ocid=$(prop 'mounttarget.ocid') +region_short=$(echo "$region" | sed 's/.*-\([a-z]*\)-.*/\1/') # generate terraform configuration file with name $(clusterTFVarsFile).tfvar #generateTFVarFile diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars b/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars index 3a938d76f78..c9ec27a4094 100755 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/template.tfvars @@ -35,7 +35,7 @@ mount_target_ocid="@MOUNTTARGETOCID@" # ## For regions, # Use short form e.g. ashburn from location column https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm # ## VCN, Pods and services clusters must not overlap with each other and with those of other clusters. clusters = { - c1 = { region = "phoenix", vcn = "10.1.0.0/16", pods = "10.201.0.0/16", services = "10.101.0.0/16", enabled = true } + c1 = { region = "@REGIONSHORT@", vcn = "10.1.0.0/16", pods = "10.201.0.0/16", services = "10.101.0.0/16", enabled = true } } # kubernetes_version = "@OKEK8SVERSION@" From abf073269383403663d6e7ce0783bd1ca1e40435 Mon Sep 17 00:00:00 2001 From: jshum Date: Wed, 23 Oct 2024 11:19:48 -0500 Subject: [PATCH 203/356] fix npe --- .../kubernetes/operator/DomainResourcesValidation.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java b/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java index c2ce491d875..4b97423446e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainResourcesValidation.java @@ -31,6 +31,7 @@ import oracle.kubernetes.operator.helpers.PodDisruptionBudgetHelper; import oracle.kubernetes.operator.helpers.PodHelper; import oracle.kubernetes.operator.helpers.ServiceHelper; +import oracle.kubernetes.operator.work.FiberGate; import oracle.kubernetes.operator.work.Packet; import oracle.kubernetes.weblogic.domain.model.ClusterList; import oracle.kubernetes.weblogic.domain.model.ClusterResource; @@ -220,7 +221,12 @@ private void updateDeletedDomainsInDPI(DomainList list) { } private boolean isNotBeingProcessed(String namespace, String domainUid) { - return processor.getMakeRightFiberGateMap().get(namespace).getCurrentFibers().get(domainUid) == null; + return Optional.ofNullable(processor) + .map(DomainProcessor::getMakeRightFiberGateMap) + .map(m -> m.get(namespace)) + .map(FiberGate::getCurrentFibers) + .map(f -> f.get(domainUid)) + .isEmpty(); } private void addDomain(DomainResource domain) { From 8b2fdd352791883325131f5f317e43f53a9a52c7 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Thu, 24 Oct 2024 19:28:03 +0000 Subject: [PATCH 204/356] Fix test failures in crio-pipeline and OCNE with podman --- .../ItDiagnosticsFailedCondition.java | 14 ++- .../kubernetes/ItIstioMonitoringExporter.java | 4 +- .../kubernetes/ItLBTwoDomainsTraefik.java | 5 + .../kubernetes/ItMiiAuxiliaryImage.java | 2 +- .../kubernetes/ItMiiDynamicUpdatePart3.java | 3 +- .../kubernetes/ItMiiUpdateDomainConfig.java | 29 +++-- .../ItMonitoringExporterMetricsFiltering.java | 1 - .../kubernetes/ItMultiDomainModels.java | 104 +++++++++--------- .../kubernetes/ItProductionSecureMode.java | 5 +- .../weblogic/kubernetes/ItRemoteConsole.java | 6 +- .../kubernetes/utils/CommonLBTestUtils.java | 4 +- .../kubernetes/utils/CommonMiiTestUtils.java | 4 +- .../kubernetes/utils/CommonTestUtils.java | 39 +++++-- 13 files changed, 133 insertions(+), 87 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java index 8b26194bc9d..85a9b7cb6de 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java @@ -63,6 +63,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; @@ -714,6 +715,7 @@ void testIntrospectorTimeoutFailure() { * type: Completed, status: false */ @DisabledIfEnvironmentVariable(named = "ARM", matches = "true") + @DisabledIfEnvironmentVariable(named = "OCNE", matches = "true") @Test @DisplayName("Test domain status condition with managed server boot failure.") void testMSBootFailureStatus() { @@ -850,11 +852,13 @@ rcuSchemaPrefix, domainNamespace, getNextFreePort(), dbUrl, dbListenerPort), if (!testPassed) { LoggingUtil.generateLog(this, ns); } - if (assertDoesNotThrow(() -> clusterExists(clusterResName, CLUSTER_VERSION, domainNamespace).call())) { - deleteClusterCustomResource(clusterResName, domainNamespace); - } - if (assertDoesNotThrow(() -> domainExists(domainName, DOMAIN_VERSION, domainNamespace).call())) { - deleteDomainResource(domainNamespace, domainName); + if (!SKIP_CLEANUP) { + if (assertDoesNotThrow(() -> clusterExists(clusterResName, CLUSTER_VERSION, domainNamespace).call())) { + deleteClusterCustomResource(clusterResName, domainNamespace); + } + if (assertDoesNotThrow(() -> domainExists(domainName, DOMAIN_VERSION, domainNamespace).call())) { + deleteDomainResource(domainNamespace, domainName); + } } } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java index 058996ce249..d60a459b44d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java @@ -80,7 +80,6 @@ @IntegrationTest @Tag("oke-parallel") @Tag("kind-parallel") -@Tag("olcne-mrg") class ItIstioMonitoringExporter { private static String opNamespace = null; @@ -254,8 +253,7 @@ private void deployPrometheusAndVerify(String domainNamespace, String domainUid, "Can't modify Prometheus CM, not possible to monitor " + domainUid); } - checkMetricsViaPrometheus(searchKey, "sessmigr", - hostPortPrometheus); + checkMetricsViaPrometheus(searchKey, "sessmigr", hostPortPrometheus); } @AfterAll diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java index de3a08afd16..67938a8b7f2 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java @@ -31,10 +31,13 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; @@ -276,6 +279,8 @@ private static void createTraefikIngressRoutingRules(String domainNamespace) { private int getTraefikLbNodePort(boolean isHttps) { if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { return isHttps ? TRAEFIK_INGRESS_HTTPS_HOSTPORT : TRAEFIK_INGRESS_HTTP_HOSTPORT; + } else if (OCNE && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + return isHttps ? TRAEFIK_INGRESS_HTTPS_NODEPORT : TRAEFIK_INGRESS_HTTP_NODEPORT; } else if (traefikHelmParams != null) { logger.info("Getting web node port for Traefik loadbalancer {0}", traefikHelmParams.getReleaseName()); return assertDoesNotThrow(() -> diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java index 94afa42caa5..9a6a7c2c668 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiAuxiliaryImage.java @@ -412,7 +412,7 @@ void testUpdateBaseImageName() { String imageTag = getDateAndTimeStamp(); String imageUpdate = KIND_REPO != null ? KIND_REPO + (WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag).substring(TestConstants.BASE_IMAGES_REPO.length() + 1) - : DOMAIN_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag; + : DOMAIN_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + "-dev:" + imageTag; imageTag(imageName, imageUpdate); imageRepoLoginAndPushImageToRegistry(imageUpdate); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java index 4839151bb0e..5022bcfd94c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart3.java @@ -29,6 +29,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.MII_DYNAMIC_UPDATE_EXPECTED_ERROR_MSG; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_RELEASE_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; @@ -327,7 +328,7 @@ void testMiiChangeDataSourceParameterWithCommitUpdateAndRoll() { verifyPodIntrospectVersionUpdated(pods.keySet(), introspectVersion, helper.domainNamespace); // check datasource configuration using REST api - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { assertTrue(checkSystemResourceConfigViaAdminPod(helper.adminServerPodName, helper.domainNamespace, "JDBCSystemResources/TestDataSource2/JDBCResource/JDBCDataSourceParams", "jdbc\\/TestDataSource2-2"), "JDBCSystemResource JNDIName not found"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index a65dc22844a..2d952a59d6f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -54,6 +54,7 @@ import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; @@ -279,8 +280,12 @@ void testMiiCustomEnv() { int adminServiceNodePort = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); - String hostAndPort = - OKE_CLUSTER ? adminServerPodName + ":7001" : getHostAndPort(adminSvcExtHost, adminServiceNodePort); + String hostAndPort; + if (OKE_CLUSTER || OCNE) { + hostAndPort = adminServerPodName + ":7001"; + } else { + hostAndPort = getHostAndPort(adminSvcExtHost, adminServiceNodePort); + } // use traefik LB for kind cluster with ingress host header in url String headers = ""; @@ -302,7 +307,7 @@ void testMiiCustomEnv() { .append("?fields=notes&links=none\"") .append(" --silent ").toString(); - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { curlString = KUBERNETES_CLI + " exec -n " + domainNamespace + " " + adminServerPodName + " -- " + curlString; } @@ -372,7 +377,7 @@ void testMiiCheckSystemResources() { = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid"); - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { String resourcePath = "/management/weblogic/latest/domainConfig/JDBCSystemResources/TestDataSource"; ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName,7001, resourcePath); assertEquals(0, result.exitValue(), "Failed to find the JDBCSystemResource configuration"); @@ -477,7 +482,7 @@ void testMiiDeleteSystemResources() { checkServiceExists(managedServerPrefix + i, domainNamespace); } - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { String resourcePath = "/management/weblogic/latest/domainConfig/JDBCSystemResources/TestDataSource"; ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); assertEquals(0, result.exitValue(), "Failed to delete the JDBCSystemResource configuration"); @@ -557,7 +562,7 @@ void testMiiAddSystemResources() { checkServiceExists(managedServerPrefix + i, domainNamespace); } - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { String resourcePath = "/management/weblogic/latest/domainConfig/JDBCSystemResources/TestDataSource2"; ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); assertEquals(0, result.exitValue(), "Failed to find the JDBCSystemResource configuration"); @@ -892,7 +897,7 @@ void testMiiDeleteSystemResourcesByEmptyConfigMap() { checkServiceExists(managedServerPrefix + i, domainNamespace); } - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { String resourcePath = "/management/weblogic/latest/domainConfig/JDBCSystemResources/TestDataSource"; ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); assertEquals(0, result.exitValue(), "Failed to delete the JDBCSystemResource configuration"); @@ -1024,8 +1029,12 @@ private static DomainResource createDomainResource( private void verifyManagedServerConfiguration(String managedServer) { int adminServiceNodePort = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); - String hostAndPort = - OKE_CLUSTER ? adminServerPodName + ":7001" : getHostAndPort(adminSvcExtHost, adminServiceNodePort); + String hostAndPort; + if (OKE_CLUSTER || OCNE) { + hostAndPort = adminServerPodName + ":7001"; + } else { + hostAndPort = getHostAndPort(adminSvcExtHost, adminServiceNodePort); + } // use traefik LB for kind cluster with ingress host header in url String headers = ""; @@ -1047,7 +1056,7 @@ private void verifyManagedServerConfiguration(String managedServer) { StringBuffer checkCluster = new StringBuffer(); - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { checkCluster = new StringBuffer(KUBERNETES_CLI) .append(" exec -n ") .append(domainNamespace) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java index 50540592af7..5e53a6e18ca 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java @@ -89,7 +89,6 @@ */ @DisplayName("Verify WebLogic Metric is processed and filtered as expected by MonitoringExporter") @IntegrationTest -@Tag("olcne-mrg") @Tag("oke-sequential") @Tag("kind-sequential") @Tag("okd-wls-mrg") diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java index ee73e93ba8a..17ab312907c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModels.java @@ -30,6 +30,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_WDT_MODEL_FILE; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; @@ -148,60 +149,63 @@ public static void initAll(@Namespaces(5) List namespaces) { + "verify admin server is accessible via REST interface.") @ValueSource(strings = {"modelInImage", "domainInImage", "domainOnPV", "auxiliaryImageDomain"}) void testScaleClustersAndAdminRESTAccess(String domainType) { - DomainResource domain = createDomainBasedOnDomainType(domainType); - - // get the domain properties - String domainUid = domain.getSpec().getDomainUid(); - String domainNamespace = domain.getMetadata().getNamespace(); - - String dynamicServerPodName = domainUid + "-managed-server1"; - OffsetDateTime dynTs = getPodCreationTime(domainNamespace, dynamicServerPodName); - final String managedServerPrefix = domainUid + "-managed-server"; - int numberOfServers = 3; - logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers.", - clusterName, domainUid, domainNamespace, numberOfServers); - assertDoesNotThrow(() -> scaleCluster(clusterName,domainNamespace, - numberOfServers), "Could not scale up the cluster"); - // check managed server pods are ready - for (int i = 1; i <= numberOfServers; i++) { - logger.info("Wait for managed server pod {0} to be ready in namespace {1}", - managedServerPrefix + i, domainNamespace); - checkPodReadyAndServiceExists(managedServerPrefix + i, domainUid, domainNamespace); - } + // workaround OWLS-122679 + if (!(OCNE && domainType.equals("domainInImage"))) { + DomainResource domain = createDomainBasedOnDomainType(domainType); + + // get the domain properties + String domainUid = domain.getSpec().getDomainUid(); + String domainNamespace = domain.getMetadata().getNamespace(); + + String dynamicServerPodName = domainUid + "-managed-server1"; + OffsetDateTime dynTs = getPodCreationTime(domainNamespace, dynamicServerPodName); + final String managedServerPrefix = domainUid + "-managed-server"; + int numberOfServers = 3; + logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers.", + clusterName, domainUid, domainNamespace, numberOfServers); + assertDoesNotThrow(() -> scaleCluster(clusterName, domainNamespace, + numberOfServers), "Could not scale up the cluster"); + // check managed server pods are ready + for (int i = 1; i <= numberOfServers; i++) { + logger.info("Wait for managed server pod {0} to be ready in namespace {1}", + managedServerPrefix + i, domainNamespace); + checkPodReadyAndServiceExists(managedServerPrefix + i, domainUid, domainNamespace); + } - Callable isDynRestarted = - assertDoesNotThrow(() -> isPodRestarted(dynamicServerPodName, domainNamespace, dynTs)); - assertFalse(assertDoesNotThrow(isDynRestarted::call), - "Dynamic managed server pod must not be restated"); - - // then scale cluster back to 1 server - logger.info("Scaling back cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", - clusterName, domainUid, domainNamespace,numberOfServers,replicaCount); - assertDoesNotThrow(() -> scaleCluster(clusterName, domainNamespace, - replicaCount), "Could not scale down the cluster"); - - for (int i = numberOfServers; i > replicaCount; i--) { - logger.info("Wait for managed server pod {0} to be deleted in namespace {1}", - managedServerPrefix + i, domainNamespace); - checkPodDeleted(managedServerPrefix + i, domainUid, domainNamespace); - } + Callable isDynRestarted = + assertDoesNotThrow(() -> isPodRestarted(dynamicServerPodName, domainNamespace, dynTs)); + assertFalse(assertDoesNotThrow(isDynRestarted::call), + "Dynamic managed server pod must not be restated"); + + // then scale cluster back to 1 server + logger.info("Scaling back cluster {0} of domain {1} in namespace {2} from {3} servers to {4} servers.", + clusterName, domainUid, domainNamespace, numberOfServers, replicaCount); + assertDoesNotThrow(() -> scaleCluster(clusterName, domainNamespace, + replicaCount), "Could not scale down the cluster"); + + for (int i = numberOfServers; i > replicaCount; i--) { + logger.info("Wait for managed server pod {0} to be deleted in namespace {1}", + managedServerPrefix + i, domainNamespace); + checkPodDeleted(managedServerPrefix + i, domainUid, domainNamespace); + } - logger.info("Validating WebLogic admin server access by REST"); - if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostHeader = createIngressHostRouting(domainNamespace, domainUid, adminServerName, 7001); - assertDoesNotThrow(() - -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); - } else { - String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; - try { - verifyAdminServerRESTAccessInAdminPod(adminServerPodName, "7001", - domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); - } catch (IOException ex) { - logger.severe(ex.getMessage()); + logger.info("Validating WebLogic admin server access by REST"); + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + hostHeader = createIngressHostRouting(domainNamespace, domainUid, adminServerName, 7001); + assertDoesNotThrow(() + -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); + } else { + String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; + try { + verifyAdminServerRESTAccessInAdminPod(adminServerPodName, "7001", + domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + } catch (IOException ex) { + logger.severe(ex.getMessage()); + } } + // shutdown domain and verify the domain is shutdown + shutdownDomainAndVerify(domainNamespace, domainUid, replicaCount); } - // shutdown domain and verify the domain is shutdown - shutdownDomainAndVerify(domainNamespace, domainUid, replicaCount); } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java index dabdb308976..85ac328a55b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItProductionSecureMode.java @@ -48,6 +48,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_DEPLOYMENT_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.SSL_PROPERTIES; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; @@ -358,9 +359,9 @@ void testMiiDynamicChangeWithSSLEnabled() { + MANAGED_SERVER_NAME_BASE + "1" + "/applicationRuntimes/" + MII_BASIC_APP_DEPLOYMENT_NAME + "/workManagerRuntimes/newWM"; - if (OKE_CLUSTER) { + if (OKE_CLUSTER || OCNE) { ExecResult result = exeAppInServerPod(domainNamespace, managedServerPrefix + "1",9002, resourcePath); - logger.info("result in OKE_CLUSTER is {0}", result.toString()); + logger.info("result in OKE_CLUSTER or OCNE cluster is {0}", result.toString()); assertEquals(0, result.exitValue(), "Failed to access WebLogic rest endpoint"); } else { testUntil( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java index 684df8c5770..cd0ef988bfa 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java @@ -47,6 +47,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; @@ -386,7 +387,10 @@ private static void createNginxIngressPathRoutingRules() throws UnknownHostExcep } else if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { nginxNodePort = assertDoesNotThrow(() -> getServiceNodePort(nginxNamespace, nginxServiceName, "http"), "Getting Nginx loadbalancer service node port failed"); + } else if (OCNE && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + nginxNodePort = IT_REMOTECONSOLENGINX_INGRESS_HTTP_NODEPORT; } + logger.info("nginxNodePort is {0}", nginxNodePort); String host = formatIPv6Host(K8S_NODEPORT_HOST); @@ -419,7 +423,7 @@ private static void installNgnixIngressController() throws UnknownHostException if (WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { logger.info("Installing Ngnix controller using 0 as nodeport"); nginxHelmParams = installAndVerifyNginx(nginxNamespace, 0, 0); - } else if (KIND_CLUSTER && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { + } else if ((KIND_CLUSTER || OCNE) && !WLSIMG_BUILDER.equals(WLSIMG_BUILDER_DEFAULT)) { logger.info("Installing Ngnix controller using http_nodeport {0}, https_nodeport {1}", IT_REMOTECONSOLENGINX_INGRESS_HTTP_NODEPORT, IT_REMOTECONSOLENGINX_INGRESS_HTTPS_NODEPORT); nginxHelmParams = installAndVerifyNginx(nginxNamespace, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java index 68f0f177f01..741ecbc05dd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java @@ -56,6 +56,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_PORT_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.CRIO; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS; import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; @@ -64,6 +65,7 @@ import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PV_ROOT; @@ -215,7 +217,7 @@ public static List createMultipleDomainsSharingPVUsingWlstAndVerify(Stri getLogger().info("Getting admin service node port: {0}", serviceNodePort); getLogger().info("Validating WebLogic admin server access by login to console"); - if (OKE_CLUSTER_PRIVATEIP) { + if (OKE_CLUSTER_PRIVATEIP || OCNE || CRIO) { assertTrue(assertDoesNotThrow( () -> adminLoginPageAccessible(adminServerPodName, "7001", domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index 2c25680a230..184b75bc42c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -63,12 +63,14 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_PATCH; +import static oracle.weblogic.kubernetes.TestConstants.CRIO; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_DEPLOYMENT_NAME; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; @@ -1222,7 +1224,7 @@ public static boolean checkWeblogicMBean(String adminSvcExtHost, boolean isSecureMode, String sslChannelName) { LoggingFacade logger = getLogger(); - if (OKE_CLUSTER_PRIVATEIP) { + if (OKE_CLUSTER_PRIVATEIP || OCNE || CRIO) { return checkWeblogicMBeanInAdminPod(domainNamespace, adminServerPodName, resourcePath, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index 5c96f44a3fb..25784179215 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -60,6 +60,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.CRIO; import static oracle.weblogic.kubernetes.TestConstants.HTTPS_PROXY; import static oracle.weblogic.kubernetes.TestConstants.HTTP_PROXY; import static oracle.weblogic.kubernetes.TestConstants.INGRESS_CLASS_FILE_NAME; @@ -67,12 +68,14 @@ import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.NODE_IP; import static oracle.weblogic.kubernetes.TestConstants.NO_PROXY; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; @@ -1327,16 +1330,21 @@ public static String getHostAndPort(String hostName, int servicePort) { try { String host; - if (TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - host = K8S_NODEPORT_HOST; + String hostAndPort; + if (OCNE || CRIO) { + hostAndPort = K8S_NODEPORT_HOST + ":" + servicePort; } else { - if (servicePort >= 30500 && servicePort <= 30600) { - servicePort -= 29000; + if (TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + host = K8S_NODEPORT_HOST; + } else { + if (servicePort >= 30500 && servicePort <= 30600) { + servicePort -= 29000; + } + host = InetAddress.getLocalHost().getHostAddress(); } - host = InetAddress.getLocalHost().getHostAddress(); + host = formatIPv6Host(host); + hostAndPort = ((OKD) ? hostName : host + ":" + servicePort); } - host = formatIPv6Host(host); - String hostAndPort = ((OKD) ? hostName : host + ":" + servicePort); logger.info("hostAndPort = {0} ", hostAndPort); if (OKE_CLUSTER_PRIVATEIP) { hostAndPort = hostName; @@ -2411,10 +2419,19 @@ public static String createIngressHostRouting(String domainNamespace, String dom .as(String.format("Test ingress %s was found in namespace %s", ingressName, domainNamespace)) .withFailMessage(String.format("Ingress %s was not found in namespace %s", ingressName, domainNamespace)) .contains(ingressName); - String curlCmd = assertDoesNotThrow(() -> "curl -g -k --silent --show-error --noproxy '*' -H 'host: " - + ingressHost + "' " + (isSecureMode ? "https" : "http") + "://" - + formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + +TRAEFIK_INGRESS_HTTP_HOSTPORT - + "/weblogic/ready --write-out %{http_code} -o /dev/null"); + + String curlCmd; + if (OCNE) { + curlCmd = assertDoesNotThrow(() -> "curl -g -k --silent --show-error --noproxy '*' -H 'host: " + + ingressHost + "' " + (isSecureMode ? "https" : "http") + "://" + + K8S_NODEPORT_HOST + ":" + TRAEFIK_INGRESS_HTTP_NODEPORT + + "/weblogic/ready --write-out %{http_code} -o /dev/null"); + } else { + curlCmd = assertDoesNotThrow(() -> "curl -g -k --silent --show-error --noproxy '*' -H 'host: " + + ingressHost + "' " + (isSecureMode ? "https" : "http") + "://" + + formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT + + "/weblogic/ready --write-out %{http_code} -o /dev/null"); + } getLogger().info("Executing curl command {0}", curlCmd); assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); From fc1fce77e0fc32c70398c1e0abcba06be759dd59 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 28 Oct 2024 16:39:24 +0000 Subject: [PATCH 205/356] Added more checks for OKE cluster connectivity failures --- Jenkinsfile.oke | 2 +- .../oke/terraform/okemodule/oke.create.sh | 57 ++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index bd60aa39bf4..96617571bcc 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -422,7 +422,7 @@ EOF export OCI_CLI_CONFIG_FILE=${jenkins_home_directory}/.oci/config export OCI_CLI_PROFILE=${oci_profile} - + oci setup repair-file-permissions --file ${jenkins_home_directory}/.oci/config echo 'Create a OKE cluster ${CLUSTER_NAME}' cp -rf ${terraform_script_dir_name}/* ${WORKSPACE}/terraform/. diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh index f1989bfaed8..f5689911eff 100755 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh @@ -93,10 +93,40 @@ checkKubernetesCliConnection() { exit 1 fi echo "clusterPublicIP: ###$clusterPublicIP###" - unset NO_PROXY - export NO_PROXY=localhost,127.0.0.1,10.244.0.0/16,10.101.0.0/16,10.196.0.0/16,$clusterPublicIP + echo " NO_PROXY=#$NO_PROXY# " + export NO_PROXY=$NO_PROXY,localhost,127.0.0.1,$clusterPublicIP echo "export NO_PROXY=:$NO_PROXY" + # Maximum number of retries + max_retries=10 + + # Initial retry count + retry_count=0 + + # Command to get cluster info + while [[ $retry_count -lt $max_retries ]]; do + echo "Attempt $((retry_count+1)) of $max_retries to connect to Kubernetes cluster..." + + # Try to execute kubectl cluster-info + ${KUBERNETES_CLI:-kubectl} cluster-info + if [[ $? -eq 0 ]]; then + echo "Connected to Kubernetes cluster successfully!" + break + else + echo "Connection refused or failed, retrying..." + retry_count=$((retry_count + 1)) + sleep 5 # Wait 5 seconds before retrying + fi + done + + # Check if retries were exhausted + if [[ $retry_count -eq $max_retries ]]; then + echo "Failed to connect to Kubernetes cluster after $max_retries attempts." + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + createCluster + fi + local myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then @@ -124,6 +154,26 @@ checkKubernetesCliConnection() { } checkClusterRunning() { + kubeconfig_file=${terraformVarDir}/${okeclustername}_kubeconfig + export KUBECONFIG=${terraformVarDir}/${okeclustername}_kubeconfig + echo "Kubeconfig file : $KUBECONFIG" + ls -al $KUBECONFIG + + if [ -f "$kubeconfig_file" ] && [ -s "$kubeconfig_file" ]; then + echo "Kubeconfig file exists and is not empty." + else + if [ ! -f "$kubeconfig_file" ]; then + echo "Kubeconfig file does not exist." + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + createCluster + else + echo "Kubeconfig file exists but is empty." + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + createCluster + fi + fi checkKubernetesCliConnection local privateIP=${vcn_cidr_prefix} @@ -210,7 +260,8 @@ setupTerraform deleteOlderVersionTerraformOCIProvider chmod 600 ${ocipk_path} - +sudo yum reinstall ca-certificates -y +sudo iptables -A OUTPUT -p tcp --dport 6443 -j ACCEPT # run terraform init,plan,apply to create OKE cluster based on the provided tfvar file ${clusterTFVarsFile).tfvar createCluster #check status of OKE cluster nodes, destroy if can not access them From 19cb01e1fc7e26cf1ea0aaeeeab7e7d252914196 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Mon, 28 Oct 2024 18:33:05 +0000 Subject: [PATCH 206/356] Add DomainOnPV configuration to Operator versions in Upgrade tests for WLS domains --- .../ItOperatorUpgradeWithIstio.java | 51 +++-- .../kubernetes/ItOperatorWlsUpgrade.java | 196 +++++++++++++++--- .../upgrade/istio.config.template.yaml | 31 ++- 3 files changed, 208 insertions(+), 70 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java index 41f9948a783..fbd1dbd45b0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorUpgradeWithIstio.java @@ -21,7 +21,6 @@ import oracle.weblogic.kubernetes.logging.LoggingFacade; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -73,7 +72,7 @@ /** * Install a released version of Operator from GitHub chart repository. * Create a domain using Model-In-Image model with a dynamic cluster. - * Configure Itsio on the domain resource with v8 schema + * Configure Itsio on the domain resource with v9 schema * Make sure the console is accessible thru istio ingress port * Upgrade operator with current Operator image build from current branch. * Make sure the console is accessible thru istio ingress port @@ -119,35 +118,35 @@ public void beforeEach(@Namespaces(2) List namespaces) { } /** - * Upgrade from Operator 3.4.3 to current with Istio enabled domain. + * Upgrade from Operator 4.0.9 to current with Istio enabled domain. */ @Test - @DisplayName("Upgrade 3.4.3 Istio Domain(v8) with Istio to current") - void testOperatorWlsIstioDomainUpgradeFrom343ToCurrent() { - logger.info("Starting testOperatorWlsIstioDomainUpgradeFrom343ToCurrent" - + " to upgrade Istio Image Domain with Istio with v8 schema to current"); - upgradeWlsIstioDomain("3.4.3"); + @DisplayName("Upgrade 4.0.9 Istio Domain with Istio to current") + void testOperatorWlsIstioDomainUpgradeFrom409ToCurrent() { + logger.info("Starting testOperatorWlsIstioDomainUpgradeFrom409ToCurrent" + + " to upgrade Istio Image Domain with Istio with v9 schema to current"); + upgradeWlsIstioDomain("4.0.9"); } /** - * Upgrade from Operator 3.4.4 to current with Istio enabled domain. + * Upgrade from Operator 4.1.7 to current with Istio enabled domain. */ @Test - @DisplayName("Upgrade 3.4.4 Istio Domain(v8) with Istio to current") - void testOperatorWlsIstioDomainUpgradeFrom344ToCurrent() { - logger.info("Starting testOperatorWlsIstioDomainUpgradeFrom344ToCurrent" - + " to upgrade Istio Image Domain with Istio with v8 schema to current"); - upgradeWlsIstioDomain("3.4.4"); + @DisplayName("Upgrade 4.1.7 Istio Domain(v9) with Istio to current") + void testOperatorWlsIstioDomainUpgradeFrom417ToCurrent() { + logger.info("Starting testOperatorWlsIstioDomainUpgradeFrom417ToCurrent" + + " to upgrade Istio Image Domain with Istio with v9 schema to current"); + upgradeWlsIstioDomain("4.1.7"); } /** - * Upgrade from Operator v3.3.8 to current with Istio enabled domain. + * Upgrade from Operator v4.2.6 to current with Istio enabled domain. */ - @Disabled - @DisplayName("Upgrade 3.3.8 Istio Domain(v8) with Istio to current") - void testOperatorWlsIstioDomainUpgradeFrom338ToCurrent() { - logger.info("Starting test to upgrade Istio Image Domain with Istio with v8 schema to current"); - upgradeWlsIstioDomain("3.3.8"); + @Test + @DisplayName("Upgrade 4.2.6 Istio Domain with Istio to current") + void testOperatorWlsIstioDomainUpgradeFrom426ToCurrent() { + logger.info("Starting test to upgrade Istio Image Domain with Istio with v9 schema to current"); + upgradeWlsIstioDomain("4.2.6"); } /** @@ -163,8 +162,8 @@ public void tearDown() { } void upgradeWlsIstioDomain(String oldVersion) { - logger.info("Upgrade version/{0} Istio Domain(v8) to current", oldVersion); - installOldOperator(oldVersion,opNamespace,domainNamespace); + logger.info("Upgrade version/{0} Istio Domain to current", oldVersion); + installOldOperator(oldVersion, opNamespace, domainNamespace); createSecrets(); // Create the repo secret to pull base WebLogic image @@ -172,15 +171,15 @@ void upgradeWlsIstioDomain(String oldVersion) { createConfigMapAndVerify("istio-upgrade-configmap", domainUid, domainNamespace, Collections.emptyList()); - // Creating an MII domain with v8 version - // Generate a v8 version of domain.yaml file from a template file + // Creating an MII domain with v9 version + // Generate a v9 version of domain.yaml file from a template file // by replacing domain namespace, domain uid, image Map templateMap = new HashMap<>(); templateMap.put("DOMAIN_NS", domainNamespace); templateMap.put("DOMAIN_UID", domainUid); templateMap.put("MII_IMAGE", MII_BASIC_IMAGE_NAME + ":" + MII_BASIC_IMAGE_TAG); - templateMap.put("API_VERSION", "v8"); + templateMap.put("API_VERSION", "v9"); Path srcDomainFile = Paths.get(RESOURCE_DIR, "upgrade", "istio.config.template.yaml"); Path targetDomainFile = assertDoesNotThrow( @@ -199,7 +198,7 @@ void upgradeWlsIstioDomain(String oldVersion) { // wait for the domain to exist logger.info("Checking for domain custom resource in namespace {0}", domainNamespace); testUntil( - domainExists(domainUid, "v8", domainNamespace), + domainExists(domainUid, "v9", domainNamespace), logger, "domain {0} to be created in namespace {1}", domainUid, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java index ebea98d6c16..ee001e60444 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOperatorWlsUpgrade.java @@ -3,6 +3,8 @@ package oracle.weblogic.kubernetes; +import java.io.File; +import java.io.FileOutputStream; import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Path; @@ -14,9 +16,18 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - +import java.util.Properties; + +import io.kubernetes.client.custom.Quantity; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.CreateIfNotExists; +import oracle.weblogic.domain.DomainCreationImage; +import oracle.weblogic.domain.DomainOnPV; +import oracle.weblogic.domain.DomainOnPVType; +import oracle.weblogic.domain.DomainResource; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; +import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -33,9 +44,11 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_COMPLETED_TYPE; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.ENCRYPION_PASSWORD_DEFAULT; @@ -48,8 +61,10 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_WDT_MODEL_FILE; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OLD_DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; @@ -66,15 +81,22 @@ import static oracle.weblogic.kubernetes.utils.ApplicationUtils.deployAndAccessApplication; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminConsoleAccessible; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createPushAuxiliaryImageWithDomainConfig; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.verifyPodsNotRolled; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainResourceOnPv; import static oracle.weblogic.kubernetes.utils.DomainUtils.verifyDomainStatusConditionTypeDoesNotExist; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; +import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; +import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchServerStartPolicy; @@ -82,6 +104,7 @@ import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; import static oracle.weblogic.kubernetes.utils.PodUtils.getExternalServicePodName; import static oracle.weblogic.kubernetes.utils.PodUtils.getPodCreationTime; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static oracle.weblogic.kubernetes.utils.UpgradeUtils.checkCrdVersion; @@ -162,6 +185,46 @@ void testOperatorUpgradeMiiDomainV8From409ToCurrent() { installOperatorCreateMiiDomainAndUpgrade("4.0.9", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } + /** + * Operator upgrade from 4.2.9 to current with DPV domain in V9 schema. + */ + @Test + @DisplayName("Upgrade Operator from 4.2.9 to current") + void testOperatorUpgradeDomainOnPVV9From429ToCurrent() { + logger.info("Starting test testOperatorUpgradeDomainOnPVV9From429ToCurrent, domain v9 schema"); + installOperatorCreatesPvPvcWlsDomainAndUpgrade("4.2.9"); + } + + /** + * Operator upgrade from 4.2.8 to current with DPV domain in V9 schema. + */ + @Test + @DisplayName("Upgrade Operator from 4.2.8 to current") + void testOperatorUpgradeDomainOnPVV9From428ToCurrent() { + logger.info("Starting test testOperatorUpgradeDomainOnPVV9From428ToCurrent, domain v9 schema"); + installOperatorCreatesPvPvcWlsDomainAndUpgrade("4.2.8"); + } + + /** + * Operator upgrade from 4.1.8 to current with DPV domain in V9 schema. + */ + @Test + @DisplayName("Upgrade Operator from 4.1.8 to current") + void testOperatorUpgradeDomainOnPVV9From418ToCurrent() { + logger.info("Starting test testOperatorUpgradeDomainOnPVV9From418ToCurrent, domain v9 schema"); + installOperatorCreatesPvPvcWlsDomainAndUpgrade("4.1.8"); + } + + /** + * Operator upgrade from 4.1.7 to current with DPV domain in V9 schema. + */ + @Test + @DisplayName("Upgrade Operator from 4.1.7 to current") + void testOperatorUpgradeDomainOnPVV9From417ToCurrent() { + logger.info("Starting test testOperatorUpgradeDomainOnPVV9From417ToCurrent, domain v9 schema"); + installOperatorCreatesPvPvcWlsDomainAndUpgrade("4.1.7"); + } + /** * Upgrade Operator from 4.0.10 to current with Auxiliary image domain, V9 schema. */ @@ -172,7 +235,6 @@ void testOperatorUpgradeAuxDomainV9From4010ToCurrent() { installOperatorCreateAuxDomainAndUpgrade("4.0.10", DOMAIN_VERSION); } - /** * Upgrade Operator from 4.1.7 to current with Mii domain in V8 schema. */ @@ -216,29 +278,6 @@ void testOperatorUpgradeMiiDomainV8From426ToCurrent() { installOperatorCreateMiiDomainAndUpgrade("4.2.6", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); } - /** - * Operator upgrade from 3.4.13 to current with Auxiliary Image Domain, V8 schema. - * V9 schema is from Operator 4.0. - */ - @Test - @DisplayName("Upgrade 3.4.13 Auxiliary Domain(v8 schema) Image to current") - void testOperatorUpgradeAuxDomainV8From3413ToCurrent() { - logger.info("Starting testOperatorUpgradeAuxDomainV8From3413ToCurrent " - + "to upgrade Domain with Auxiliary Image with v8 schema to current"); - installOperatorCreateAuxDomainAndUpgrade("3.4.13", OLD_DOMAIN_VERSION); - } - - /** - * Upgrade Operator from 3.4.12 to current with Mii domain in V8 schema. - */ - @Test - @DisplayName("Upgrade 3.4.12 Mii Domain(v8 schema) Image to current") - void testOperatorUpgradeMiiDomainV8From3412ToCurrent() { - logger.info("Starting testOperatorWlsAuxDomainV8UpgradeFrom3412ToCurrent " - + "to upgrade MII Domain with v8 schema to current"); - installOperatorCreateMiiDomainAndUpgrade("3.4.12", OLD_DOMAIN_VERSION, DEFAULT_EXTERNAL_SERVICE_NAME_SUFFIX); - } - /** * Cleanup Kubernetes artifacts in the namespaces used by the test and * delete CRD. @@ -323,6 +362,93 @@ void installOperatorCreateAuxDomainAndUpgrade(String operatorVersion, String dom scaleClusterUpAndDown(domainApiVersion); } + void installOperatorCreatesPvPvcWlsDomainAndUpgrade(String operatorVersion) { + final String storageClassName = "weblogic-domain-storage-class"; + String domainHomePrefix = "/shared/" + domainNamespace + "/domains/"; + final String clusterName = "cluster-1"; + final String pvName = getUniqueName(domainUid + "-pv-"); + final String pvcName = getUniqueName(domainUid + "-pvc-"); + final int t3ChannelPort = getNextFreePort(); + final String wlsModelFile = "model-wlsdomain-onpv-simplified.yaml"; + + logger.info("Upgrade version/{0} Auxiliary Domain(v9) to current", operatorVersion); + installOldOperator(operatorVersion, opNamespace, domainNamespace); + createSecrets(); + + // Create the repo secret to pull base WebLogic image + createBaseRepoSecret(domainNamespace); + + // create a model property file + File wlsModelPropFile = createWdtPropertyFile("wlsonpv-upgrade"); + + // create domainCreationImage + String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-upgrade"; + // create image with model and wdt installation files + WitParams witParams + = new WitParams() + .modelImageName(domainCreationImageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(Collections.singletonList(MODEL_DIR + "/" + wlsModelFile)) + .modelVariableFiles(Collections.singletonList(wlsModelPropFile.getAbsolutePath())); + createAndPushAuxiliaryImage(domainCreationImageName, MII_BASIC_IMAGE_TAG, witParams); + + DomainCreationImage domainCreationImage + = new DomainCreationImage().image(domainCreationImageName + ":" + MII_BASIC_IMAGE_TAG); + + // create a domain resource + logger.info("Creating domain custom resource"); + Map pvCapacity = new HashMap<>(); + pvCapacity.put("storage", new Quantity("2Gi")); + + Map pvcRequest = new HashMap<>(); + pvcRequest.put("storage", new Quantity("2Gi")); + Configuration configuration = null; + if (OKE_CLUSTER) { + configuration = getConfiguration(pvcName, pvcRequest, "oci-fss"); + } else { + configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, + this.getClass().getSimpleName()); + } + configuration.getInitializeDomainOnPV().domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); + DomainResource domain = createDomainResourceOnPv(domainUid, + domainNamespace, + adminSecretName, + clusterName, + pvName, + pvcName, + new String[]{BASE_IMAGES_REPO_SECRET_NAME}, + domainHomePrefix, + replicaCount, + 0, + configuration); + + // Set the inter-pod anti-affinity for the domain custom resource + setPodAntiAffinity(domain); + + // create a domain custom resource and verify domain is created + createDomainAndVerify(domain, domainNamespace); + + // verify that all servers are ready + verifyDomainReady(domainNamespace, domainUid, replicaCount, "nosuffix"); + LinkedHashMap pods = new LinkedHashMap<>(); + pods.put(adminServerPodName, getPodCreationTime(domainNamespace, adminServerPodName)); + // get the creation time of the managed server pods before upgrading the operator + for (int i = 1; i <= replicaCount; i++) { + pods.put(managedServerPodNamePrefix + i, getPodCreationTime(domainNamespace, managedServerPodNamePrefix + i)); + } + // verify there is no status condition type Completed + // before upgrading to Latest + verifyDomainStatusConditionTypeDoesNotExist(domainUid, domainNamespace, + DOMAIN_STATUS_CONDITION_COMPLETED_TYPE, DOMAIN_VERSION); + upgradeOperatorToCurrent(opNamespace); + checkDomainStatus(domainNamespace, domainUid); + verifyPodsNotRolled(domainNamespace, pods); + scaleClusterUpAndDown(clusterName); + } + // After upgrade scale up/down the cluster private void scaleClusterUpAndDown(String domainApiVersion) { @@ -603,5 +729,25 @@ private void verifyDomain(String domainUidString, String domainNamespace, String String.valueOf(serviceNodePort), false); } } + + private File createWdtPropertyFile(String wlsModelFilePrefix) { + + // create property file used with domain model file + Properties p = new Properties(); + p.setProperty("adminUsername", ADMIN_USERNAME_DEFAULT); + p.setProperty("adminPassword", ADMIN_PASSWORD_DEFAULT); + + // create a model property file + File domainPropertiesFile = assertDoesNotThrow(() -> + File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), + "Failed to create WLS model properties file"); + // create the property file + assertDoesNotThrow(() -> + p.store(new FileOutputStream(domainPropertiesFile), "WLS properties file"), + "Failed to write WLS properties file"); + + return domainPropertiesFile; + } + } diff --git a/integration-tests/src/test/resources/upgrade/istio.config.template.yaml b/integration-tests/src/test/resources/upgrade/istio.config.template.yaml index 525e7c50815..aaec45c9552 100644 --- a/integration-tests/src/test/resources/upgrade/istio.config.template.yaml +++ b/integration-tests/src/test/resources/upgrade/istio.config.template.yaml @@ -28,31 +28,24 @@ spec: cpu: "250m" memory: "768Mi" adminServer: - serverStartState: RUNNING + serverStartPolicy: IfNeeded replicas: 2 clusters: - - clusterName: cluster-1 - serverPod: - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "weblogic.clusterName" - operator: In - values: - - $(CLUSTER_NAME) - topologyKey: "kubernetes.io/hostname" - replicas: 2 + - name: cluster-1 configuration: - istio: - enabled: true - localhostBindingsEnabled: false introspectorJobActiveDeadlineSeconds: 600 model: configMap: istio-upgrade-configmap domainType: WLS runtimeEncryptionSecret: encryptionsecret +--- + +apiVersion: "weblogic.oracle/v1" +kind: Cluster +metadata: + name: cluster-1 + namespace: DOMAIN_NS +spec: + clusterName: cluster-1 + replicas: 2 From a713b0ace03d2966b2ceb661c327b0462701e786 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 28 Oct 2024 17:58:13 -0400 Subject: [PATCH 207/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index b1f540dbd73..da2be2c4c02 100644 --- a/pom.xml +++ b/pom.xml @@ -684,7 +684,7 @@ 3.1.3 3.4.2 3.3.1 - 3.20.0 + 3.21.0 3.5.1 3.5.0 3.1.1 @@ -692,7 +692,7 @@ 3.5.0 3.8.0 3.6.0 - 3.4.1 + 3.5.0 10.18.2 1.0 3.5.0 @@ -721,7 +721,7 @@ 4.12.0 3.9.1 1.78.1 - 5.11.2 + 5.11.3 5.7.1 1.7.0 1.3.2 @@ -735,10 +735,10 @@ 2.18.0 2.3 2.11.0 - 10.0.4 + 11.0.0 2.0.16 1.5.11 - 4.28.2 + 4.28.3 2.5.1 9.41.2 ${project.basedir}/src-generated-swagger From 14ce38e7e2a67be3347be0a80224d1d023644ba4 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 29 Oct 2024 22:30:21 +0000 Subject: [PATCH 208/356] Merge branch 'edburns-msft-em-6234-reza-fixes' into 'main' Fixes from m_reza_rahman See merge request weblogic-cloud/weblogic-kubernetes-operator!4854 (cherry picked from commit 591f2b12360f2b576210996768715a2375b9c8c9) 16788738 Bug fixes, simplifications, and updates c7349d93 improve scripts e97b9d0c update content of domain on PV sample automation. 951ed569 clarify editing create-domain-on-aks-inputs.sh is optional eb5599f7 Merge remote-tracking branch 'oracle/main' into edburns-msft-em-6234-reza-fixes c8059271 improve script comments 49d2a7a0 increase the max wait time --- .../content/managing-domains/aks/_index.md | 38 +- .../azure-kubernetes-service/_index.md | 9 +- .../azure-kubernetes-service/domain-on-pv.md | 55 +- .../includes/aks-connect-acr.txt | 2 +- .../includes/clean-up-resources-body-01.txt | 2 +- .../includes/create-acr.txt | 2 +- .../includes/create-aks-cluster-body-01.txt | 6 +- .../includes/create-aks-cluster-body-02.txt | 2 +- .../includes/create-aks-cluster-storage.txt | 24 +- .../includes/create-resource-group.txt | 2 + .../includes/prerequisites-01.txt | 7 +- .../includes/prerequisites-02.txt | 7 +- .../run-mii-to-create-auxiliary-image.txt | 7 +- .../model-in-image.md | 55 +- .../site/static/images/aks-solution.png | Bin 0 -> 88692 bytes .../create-domain-on-aks-inputs.sh | 43 -- .../create-domain-on-aks.sh | 560 +++++++++++------- 17 files changed, 480 insertions(+), 341 deletions(-) create mode 100644 documentation/site/static/images/aks-solution.png diff --git a/documentation/site/content/managing-domains/aks/_index.md b/documentation/site/content/managing-domains/aks/_index.md index 5ad74a8cf86..c0ad0934c7b 100644 --- a/documentation/site/content/managing-domains/aks/_index.md +++ b/documentation/site/content/managing-domains/aks/_index.md @@ -9,15 +9,17 @@ description: "Deploy WebLogic Server on Azure Kubernetes Service." ### Introduction -This document is the reference documentation for the Azure Marketplace offer for WebLogic Server on Azure Kubernetes Service. The offer makes it easy to get started with WebLogic Server on Azure. The offer handles all the initial setup, creating the AKS cluster, container registry, WebLogic Kubernetes Operator installation, and domain creation using the model-in-image domain home source type. +This document is the reference for the Azure Marketplace offer for WebLogic Server on Azure Kubernetes Service. The offer makes it easy to get started with WebLogic Server on Azure. The offer handles all the initial setup, creating the AKS cluster, container registry, load-balancer, WebLogic Kubernetes Operator installation, and domain creation using the model-in-image domain home source type. To deploy the offer from the Azure portal, see [WebLogic Server on Azure](https://aka.ms/wls-aks-portal). +{{< img "WLS AKS Marketplace Solution Screenshot" "images/aks-solution.png" >}} + {{< readfile file="/samples/azure-kubernetes-service/includes/aks-value-prop.txt" >}} For complete details on domain home source types, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). -It is also possible to run the WebLogic Kubernetes Operator manually, without the aid of the Azure Marketplace offer. The steps for doing so are documented in the sample [Azure Kubernetes Service]({{< relref "/samples/azure-kubernetes-service/_index.md" >}}). +It is also possible to run the WebLogic Kubernetes Operator manually, without the aid of the Azure Marketplace offer. The steps for doing so are documented in the [Azure Kubernetes Service]({{< relref "/samples/azure-kubernetes-service/_index.md" >}}) sample. The remaining steps on this page document the user experience for the Azure Marketplace offer for WebLogic Server on Azure Kubernetes Service. @@ -93,11 +95,11 @@ In this section, you can configure the image that is deployed using the model-in #### Application -In this section you can deploy a Java EE Application along with the WebLogic Server deployment. +In this section, you can deploy an application along with WebLogic Server. | Field | Description | |-------|-------------| -| Deploy an application? | If set to **Yes**, you must specify a Java EE WAR, EAR, or JAR file suitable for deployment with the selected version of WebLogic Server. If set to **No**, no application is deployed.| +| Deploy an application? | If set to **Yes**, you must specify a WAR, EAR, or JAR file suitable for deployment with the selected version of WebLogic Server. If set to **No**, no application is deployed.| | Application package (.war,.ear,.jar) | With the **Browse** button, you can select a file from a pre-existing Azure Storage Account and Storage Container within that account. To learn how to create a Storage Account and Container, see [Create a storage account](https://docs.microsoft.com/azure/storage/common/storage-account-create?tabs=azure-portal) and [Create a Storage Container and upload application files](https://docs.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-portal). | | Number of WebLogic Managed Server replicas | The initial value of the `replicas` field of the Domain. For information, see [Scaling]({{< relref "/managing-domains/domain-lifecycle/scaling.md" >}}). | @@ -106,10 +108,10 @@ In this section you can deploy a Java EE Application along with the WebLogic Ser | Field | Description | |-------|-------------| | Show advanced configuration? | If you want to retain the default values for the optional configuration, such as **Enable Container insights**, **Create Persistent Volume using Azure File share service** and others, set the toggle button to **No**, and click **Next** to configure TLS/SSL. If you want to specify different values for the optional configuration, set the toggle button to **Yes**, and enter the following details. | -|Enable Container insights| If selected, cause the deployment to create an Azure Monitoring workspace and connect it to the AKS cluster as the Azure Monitoring Agent. Azure Monitoring Agent is a tool that collects data and sends it to Azure Container Insights. Container insights gives you performance visibility by collecting memory and processor metrics from controllers, nodes, and containers that are available in Kubernetes through the Metrics API. Container logs are also collected. Metrics are written to the metrics store and log data is written to the logs store associated with your Log Analytics workspace. For more information, see [Azure Monitor Agent overview](/azure/azure-monitor/agents/agents-overview) and [Container insights overview](https://aka.ms/wls-aks-container-insights). | +|Enable Container insights| If selected, causes the deployment to create an Azure Monitoring workspace and connect it to the AKS cluster as the Azure Monitoring Agent. Azure Monitoring Agent is a tool that collects data and sends it to Azure Container Insights. Container insights gives you performance visibility by collecting memory and processor metrics from controllers, nodes, and containers that are available in Kubernetes through the Metrics API. Container logs are also collected. Metrics are written to the metrics store and log data is written to the logs store associated with your Log Analytics workspace. For more information, see [Azure Monitor Agent overview](/azure/azure-monitor/agents/agents-overview) and [Container insights overview](https://aka.ms/wls-aks-container-insights). | |Create Persistent Volume using Azure File share service|If selected, an Azure Storage Account and an Azure Files share will be provisioned. The file system type is NFS. The name of the Azure Files share is **weblogic**. The mount point is `/shared` as a persistent volume in the nodes of the AKS cluster. For more information, see [Oracle WebLogic Server persistent storage]({{< relref "/managing-domains/persistent-storage/_index.md" >}}) and [persistent volume with Azure Files share on AKS](https://docs.microsoft.com/azure/aks/azure-files-volume).| -| Bring your own WebLogic Server Docker image from Azure Container Registry? | If check the checkbox, the subsequent options are constrained to allow only selecting from a set of pre-existing WebLogic Server Docker images stored in the Oracle Container Registry. | -| Select existing ACR instance | This option is shown only if **Bring your own WebLogic Server Docker image from Azure Container Registry?** is checked. If visible, select an existing Acure Container Registry instance. | +| Bring your own WebLogic Server Docker image from Azure Container Registry? | If the checkbox is selected, you can use your own WebLogic Server Docker image from a pre-existing Azure Container Registry instance. | +| Select existing ACR instance | This option is shown only if **Bring your own WebLogic Server Docker image from Azure Container Registry?** is checked. If visible, select an existing Azure Container Registry instance. | | Please provide the image path | This option is shown only if **Bring your own WebLogic Server Docker image from Azure Container Registry?** is checked. If visible, the value must be a fully qualified Docker tag of an image within the specified ACR. | When you are satisfied with your selections, select **Next** and open **TLS/SSL** blade. @@ -118,7 +120,7 @@ When you are satisfied with your selections, select **Next** and open **TLS/SSL* With the **TLS/SSL** blade, you can configure Oracle WebLogic Server Administration Console on a secure HTTPS port, with your own SSL certificate provided by a Certifying Authority (CA). See [Oracle WebLogic Server Keystores configuration](https://aka.ms/arm-oraclelinux-wls-ssl-configuration) for more information. -Select **Yes** or **No** for the option **Configure WebLogic Server Administration Console, Remote Console, and cluster to use HTTPS (Secure) ports, with your own TLS/SSL certificate.** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by selecting **Next**. If you select **Yes**, you can choose to provide the required configuration details by either uploading existing keystores or by using keystores stored in Azure Key Vault. +Select **Yes** or **No** for the option **Configure WebLogic Server Administration Console, Remote Console, and cluster to use HTTPS (Secure) ports, with your own TLS/SSL certificate.** If you select **No**, you don't have to provide any details, and can proceed by selecting **Next**. If you select **Yes**, you can choose to provide the required configuration details by either uploading existing keystores or by using keystores stored in Azure Key Vault. If you want to upload existing keystores, select **Upload existing KeyStores** for the option **How would you like to provide required configuration**, and enter the values for the fields listed in the following table. @@ -233,7 +235,7 @@ You can fill in any valid value in this column. **Target** and **Port** column: -For the ports, the recommended values are the usual 7001 for the **admin-server** and 8001 for the **cluster-1**. +For the ports, the recommended values are the usual 7001 for **admin-server** and 8001 for **cluster-1**. When you are satisfied with your selections, select **Next** and open **DNS** blade. @@ -280,7 +282,7 @@ Use the Database blade to configure Oracle WebLogic Server to connect to an exis | JNDI Name | Enter the JNDI name for your database JDBC connection. | | DataSource Connection String | Enter the JDBC connection string for your database. For information about obtaining the JDBC connection string, see [Obtain the JDBC Connection String for Your Database](https://docs.oracle.com/en/middleware/standalone/weblogic-server/wlazu/obtain-jdbc-connection-string-your-database.html#GUID-6523B742-EB68-4AF4-A85C-8B4561C133F3). | | Global transactions protocol | Determines the transaction protocol (global transaction processing behavior) for the data source. For more information, see [JDBC Data Source Transaction Options](https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/jdbca/transactions.html#GUID-4C929E67-5FD7-477B-A749-1EA0F4FD25D4). **IMPORTANT: The correct value for this parameter depends on the selected database type. For PostgreSQL, select EmulateTwoPhaseCommit**. | -| Use passwordless datasource connection | If you select a database type that supports passwordless connection, then this check box will appear. If selected, configure passwordless connections to the data source. For more information, see [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview). | +| Use passwordless datasource connection | If you select a database type that supports passwordless connection, this check box will appear. If selected, a passwordless connection to the data source will be configured. For more information, see [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview). | | Database Username | Enter the user name of your database. | | Database Password | Enter the password for the database user. | | Confirm password | Re-enter the value of the preceding field. | @@ -298,7 +300,7 @@ When you are satisfied with your selections, select **Next** and open **Autoscal ### Autoscaling -Use the Autoscaling blade to configure metric that scales the WebLogic cluster. Select **Yes** or **No** for the option **Provision resources for horizontal autoscaling?** based on your preference. If you select **No**, you don't have to provide any details, and can proceed by clicking **Review + create**. If you select **Yes**, you must specify the details of your autoscaling option and autoscaling settings. +Use the Autoscaling blade to configure metrics that scale the WebLogic cluster. Select **Yes** or **No** for the option **Provision resources for horizontal autoscaling?**, based on your preference. If you select **No**, you don't have to provide any details, and can proceed by clicking **Review + create**. If you select **Yes**, you must specify the details of your autoscaling settings. You must select one of the following two options, each described in turn. @@ -310,19 +312,19 @@ You must select one of the following two options, each described in turn. | Field | Description | |-------|-------------| | Select metric | There are two options:{{< line_break >}}{{< line_break >}} • Average CPU Utilization {{< line_break >}} • Average Memory Utilization | -| Average CPU Utilization | Pick average CPU utilization in percent. The HPA autoscales WebLogic Server instances from a minimum of 1 cluster members up to maximum of cluster members, and the scale up or down action occur when the average CPU is consistently over the utilization. | -| Average Memory Utilization | Pick average memory utilization in percent. The HPA autoscales WebLogic Server instances from a minimum of 1 cluster members up to maximum of cluster members, and the scale up or down action occur when the average memory is consistently over the utilization.| +| Average CPU Utilization | Pick average CPU utilization in percent. The HPA autoscales WebLogic Server instances from a minimum of 1 cluster member up to the maximum of cluster members, and the scale up or down action occurs when the average CPU is consistently over/under the utilization. | +| Average Memory Utilization | Pick average memory utilization in percent. The HPA autoscales WebLogic Server instances from a minimum of 1 cluster member up to the maximum of cluster members, and the scale up or down action occurs when the average memory is consistently over/under the utilization.| #### WebLogic Monitoring Exporter (advanced autoscaling) -This option installs all the software necessary to allow you to create Java metric aware KEDA scaling rules. The offer provisions the following deployments. Right-click and select **Open Link in New Tab** to follow links: +This option installs all the software necessary to allow you to create Java metric aware KEDA scaling rules. The offer provisions the following software. Right-click and select **Open Link in New Tab** to follow links and learn more: * [Install WebLogic Monitoring Exporter to scrape WebLogic Server metrics](https://aka.ms/wls-exporter). * [Install AKS Prometheus metrics addon](https://aka.ms/aks-enable-monitoring). * [Feed WebLogic Server metrics to Azure Monitor Managed Service for Prometheus](https://aka.ms/aks-prometheus-metrics-scrape-configuration). * [Integrate KEDA with AKS cluster](https://aka.ms/aks-integrate-keda). -After the provisioning is completed, you can create KEDA scaling rules. A sample rule is provided in the deployment outputs. The following steps show how to see the sample rule. +After the provisioning is complete, you can create KEDA scaling rules. A sample rule is provided in the deployment outputs. The following steps show how to see the sample rule. * View the resource group for this deployment in the Azure portal. * In the **Settings** section, select **Deployments**. @@ -337,13 +339,13 @@ When you are satisfied with your selections, select **Review + create**. In the **Review + create blade**, review the details you provided for deploying Oracle WebLogic Server on AKS. If you want to make changes to any of the fields, click **< previous** or click on the respective blade and update the details. -If you want to use this template to automate the deployment, download it by selecting **Download a template for automation**. +If you want to use the underlying template to further customize it yourself (for example, as part of your CI/CD pipeline), download it by selecting **Download a template for automation**. -Click **Create** to create this offer. This process may take 30 to 60 minutes. +Click **Create** to start the deployment. This process may take 30 to 60 minutes. ### Template outputs -After clicking **Create** to create this offer, you will go to the **Deployment is in progress** page. When the deployment is completed, the page shows **Your deployment is complete**. In the left panel, select **Outputs**. These are the outputs from the deployment. The following table is a reference guide to the deployment outputs. +After clicking **Create**, you will go to the **Deployment is in progress** page. When the deployment is complete, the page shows **Your deployment is complete**. In the left panel, select **Outputs**. These are the outputs from the deployment. The following table is a reference to the deployment outputs. | Field | Description | |-------|-------------| diff --git a/documentation/site/content/samples/azure-kubernetes-service/_index.md b/documentation/site/content/samples/azure-kubernetes-service/_index.md index 3dfbfaa3780..c26c642cc0b 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/_index.md +++ b/documentation/site/content/samples/azure-kubernetes-service/_index.md @@ -11,11 +11,10 @@ description: "Sample for using the operator to set up a WLS cluster on the Azure This sample demonstrates how to use the [WebLogic Kubernetes Operator]({{< relref "/_index.md" >}}) (hereafter "the operator") to set up a WebLogic Server (WLS) cluster on the Azure Kubernetes Service (AKS). After going through the steps, your WLS domain runs on an AKS cluster. You have several options for managing the cluster, depending on which [domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}) you choose. With Domain on PV, you can manage your WLS domain by accessing the WebLogic Server Administration Console or WLST. With Model in Image, you use the operator to perform WLS administrative operations. -**NOTE**: For an alternative approach to this sample, -see the [Oracle WebLogic Server on AKS from the Azure Marketplace]({{}}) -offering which automates the provisioning of -AKS cluster, AKS resources, the Azure Container Registry (ACR), - WebLogic Kubernetes Operator, and WebLogic Server images. +{{% notice note %}} +For an alternative approach to this sample, see the [Oracle WebLogic Server on AKS Azure Marketplace offering]({{}}), which automates the provisioning of +the AKS cluster, AKS resources, Azure Container Registry (ACR), load-balancer, WebLogic Kubernetes Operator, and WebLogic Server images. +{{% /notice %}} #### Azure Kubernetes Service cluster diff --git a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md index e45405c7cc6..e44d9cabe0c 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md +++ b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md @@ -5,7 +5,7 @@ weight: 2 description: "Sample for creating a WebLogic domain home on an existing PV or PVC on the Azure Kubernetes Service." --- -This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://oracle.github.io/weblogic-kubernetes-operator) (hereafter "the operator") to set up a WebLogic Server (WLS) cluster on the Azure Kubernetes Service (AKS) using the domain on PV approach. After going through the steps, your WLS domain runs on an AKS cluster instance and you can manage your WLS domain by accessing the WebLogic Server Administration Console. +This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://oracle.github.io/weblogic-kubernetes-operator) (hereafter "the operator") to set up a WebLogic Server (WLS) cluster on the Azure Kubernetes Service (AKS) using the domain on PV approach. After going through the steps, your WLS domain runs on an AKS cluster and you can manage your WLS domain by accessing the WebLogic Server Administration Console. #### Contents @@ -32,6 +32,8 @@ This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://o ##### Prepare parameters +Set required parameters by running the following commands. + ```shell # Change these parameters as needed for your own environment export ORACLE_SSO_EMAIL= @@ -59,11 +61,11 @@ export ACR_NAME="${NAME_PREFIX}acr${TIMESTAMP}" {{< readfile file="/samples/azure-kubernetes-service/includes/sign-in-azure.txt" >}} +{{< readfile file="/samples/azure-kubernetes-service/includes/download-samples-zip.txt" >}} + {{% notice info %}} The following sections of the sample instructions will guide you, step-by-step, through the process of setting up a WebLogic cluster on AKS - remaining as close as possible to a native Kubernetes experience. This lets you understand and customize each step. If you wish to have a more automated experience that abstracts some lower level details, you can skip to the [Automation](#automation) section. {{% /notice %}} -{{< readfile file="/samples/azure-kubernetes-service/includes/download-samples-zip.txt" >}} - {{< readfile file="/samples/azure-kubernetes-service/includes/create-resource-group.txt" >}} {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt" >}} @@ -121,7 +123,7 @@ See [Understanding your first archive]({{< relref "/samples/domains/domain-home- ##### Staging a ZIP file of the archive -Delete existing archive.zip in case we have an old leftover version. +Delete any possible existing archive.zip in case we have an old leftover version. ```shell $ rm -f ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip @@ -215,7 +217,7 @@ Now that you have created the AKS cluster, installed the operator, and verified ##### Create secrets -You will use the `$BASE_DIR/sample-scripts/create-weblogic-domain-credentials/create-weblogic-credentials.sh` script to create the domain WebLogic administrator credentials as a Kubernetes secret. Please run: +You will use the `$BASE_DIR/sample-scripts/create-weblogic-domain-credentials/create-weblogic-credentials.sh` script to create the domain WebLogic administrator credentials as a Kubernetes secret. Please run the following commands: ``` cd $BASE_DIR/sample-scripts/create-weblogic-domain-credentials @@ -223,19 +225,24 @@ cd $BASE_DIR/sample-scripts/create-weblogic-domain-credentials ```shell $ ./create-weblogic-credentials.sh -u ${WEBLOGIC_USERNAME} -p ${WEBLOGIC_PASSWORD} -d domain1 ``` + +The output will show something similar to the following: + ``` secret/domain1-weblogic-credentials created secret/domain1-weblogic-credentials labeled The secret domain1-weblogic-credentials has been successfully created in the default namespace. ``` - -You will use the `kubernetes/samples/scripts/create-kubernetes-secrets/create-docker-credentials-secret.sh` script to create the Docker credentials as a Kubernetes secret. Please run: +You will use the `kubernetes/samples/scripts/create-kubernetes-secrets/create-docker-credentials-secret.sh` script to create the Docker credentials as a Kubernetes secret. Please run the following commands: ``` shell $ cd $BASE_DIR/sample-scripts/create-kubernetes-secrets $ ./create-docker-credentials-secret.sh -s ${SECRET_NAME_DOCKER} -e ${ORACLE_SSO_EMAIL} -p ${ORACLE_SSO_PASSWORD} -u ${ORACLE_SSO_EMAIL} ``` + +The output will show something similar to the following: + ``` secret/wlsregcred created The secret wlsregcred has been successfully created in the default namespace. @@ -246,6 +253,9 @@ Verify secrets with the following command: ```shell $ kubectl get secret ``` + +The output will show something similar to the following: + ``` NAME TYPE DATA AGE domain1-weblogic-credentials Opaque 2 2m32s @@ -268,7 +278,7 @@ kubectl label namespace default weblogic-operator=enabled ##### Create WebLogic Domain Now, you deploy a `sample-domain1` domain resource and an associated `sample-domain1-cluster-1` cluster resource using a single YAML resource file which defines both resources. The domain resource and cluster resource tells the operator how to deploy a WebLogic domain. They do not replace the traditional WebLogic configuration files, but instead cooperate with those files to describe the Kubernetes artifacts of the corresponding domain. -- Run the following command to generate resource files. +- Run the following commands to generate resource files. Export `Domain_Creation_Image_tag`, which will be referred in `create-domain-on-aks-generate-yaml.sh`. @@ -296,12 +306,19 @@ The domain resource references the cluster resource, a WebLogic Server installat ```shell $ kubectl apply -f admin-lb.yaml ``` + + The output will show something similar to the following: + ``` service/domain1-admin-server-external-lb created ``` + ```shell $ kubectl apply -f cluster-lb.yaml ``` + + The output will show something similar to the following: + ``` service/domain1-cluster-1-external-lb created ``` @@ -363,7 +380,7 @@ The domain resource references the cluster resource, a WebLogic Server installat ``` - In the example, the URL to access the Administration Server is: `http://4.157.147.131/console`. + In the example, the URL to access the Administration Server is: `http://4.157.147.131:7001/console`. The user name and password that you enter for the Administration Console must match the ones you specified for the `domain1-weblogic-credentials` secret in the [Create secrets](#create-secrets) step. If the WLS Administration Console is still not available, use `kubectl get events --sort-by='.metadata.creationTimestamp' ` to troubleshoot. @@ -372,25 +389,20 @@ The domain resource references the cluster resource, a WebLogic Server installat $ kubectl get events --sort-by='.metadata.creationTimestamp' ``` -To access the sample application on WLS, you may skip to the section [Access sample application](#access-sample-application). The next section includes a script that automates all of the preceding steps. +To access the sample application on WLS, skip to the section [Access sample application](#access-sample-application). The next section includes a script that automates all of the preceding steps. #### Automation -If you want to automate the above steps of creating AKS cluster and WLS domain, you can use the script `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh`. +If you want to automate the above steps of creating the AKS cluster and WLS domain, you can use the script `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh`. The sample script will create a WLS domain home on the AKS cluster, including: - Creating a new Azure resource group, with a new Azure Storage Account and Azure File Share to allow WebLogic to persist its configuration and data separately from the Kubernetes pods that run WLS workloads. - Creating WLS domain home. - Generating the domain resource YAML files, which can be used to restart the Kubernetes artifacts of the corresponding domain. -For input values, you can edit `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh` directly. The following values must be specified: +To customize the WLS domain, you can optionally edit `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh`. -| Name in YAML file | Example value | Notes | -|-------------------|---------------------|------------------------------------------------------------------------------------------------| -| `dockerEmail` | `yourDockerEmail` | Oracle Single Sign-On (SSO) account email, used to pull the WebLogic Server Docker image. | -| `dockerPassword` | `yourDockerPassword` | Password for Oracle SSO account, used to pull the WebLogic Server Docker image, in clear text. | -| `weblogicUserName` | `weblogic` | Uername for WebLogic user account. | -| `weblogicAccountPassword` | `Secret123456` | Password for WebLogic user account. | +You can now run the script. ```shell $ cd ${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service @@ -400,7 +412,7 @@ $ cd ${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-servi $ ./create-domain-on-aks.sh ``` -The script will print the Administration Server address after a successful deployment. +The script will take some time to run. The script will print the Administration Server address after a successful deployment. To interact with the cluster using `kubectl`, use `az aks get-credentials` as shown in the script output. {{% notice info %}} You now have created an AKS cluster with Azure Files NFS share to contain the WLS domain configuration files. Using those artifacts, you have used the operator to create a WLS domain. @@ -421,12 +433,11 @@ Access the sample application using the cluster load balancer IP address. $ CLUSTER_IP=$(kubectl get svc domain1-cluster-1-lb -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') ``` - ```shell $ curl http://${CLUSTER_IP}:8001/myapp_war/index.jsp ``` -The test application will list the server host and server IP on the output, like the following: +The test application will list the server host on the output, like the following: ```html
    @@ -478,7 +489,7 @@ wlsstorage1612795811.file.core.windows.net:/wlsstorage1612795811/wls-weblogic-16
     
     {{< readfile file="/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt" >}}
     
    -If you created the AKS cluster step by step, run the following commands to clean up resources.
    +If you created the AKS cluster step by step, run the following command to clean up resources.
     
     {{< readfile file="/samples/azure-kubernetes-service/includes/clean-up-resources-body-02.txt" >}}
     
    diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt
    index ebf6eacc499..2ad69117b4b 100644
    --- a/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt
    +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/aks-connect-acr.txt
    @@ -1,4 +1,4 @@
    -Finally, connect AKS to the ACR.  For more details on connecting ACR to an existing AKS, see [Configure ACR integration for existing AKS clusters](https://learn.microsoft.com/en-us/azure/aks/cluster-container-registry-integration?tabs=azure-cli#configure-acr-integration-for-an-existing-aks-cluster).
    +Finally, connect the AKS cluster to the ACR. For more details on connecting ACR to an existing AKS, see [Configure ACR integration for existing AKS clusters](https://learn.microsoft.com/en-us/azure/aks/cluster-container-registry-integration?tabs=azure-cli#configure-acr-integration-for-an-existing-aks-cluster).
     
     ```shell
     $ export ACR_ID=$(az acr show -n $ACR_NAME --resource-group $AKS_PERS_RESOURCE_GROUP --query "id" -o tsv)
    diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt
    index 1253deb30a6..05ad4cf46e6 100644
    --- a/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt
    +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt
    @@ -1,4 +1,4 @@
    -The output from the `create-domain-on-aks.sh` script includes a statement about the Azure resources created by the script.  To delete the cluster and free all related resources, simply delete the resource groups.  The output will list the resource groups, such as.
    +If you used the automation script, the output from the `create-domain-on-aks.sh` script includes a statement about the Azure resources created by the script. To delete the cluster and free all related resources, simply delete the resource groups. The output will list the resource groups, such as:
     
     ```shell
     The following Azure Resouces have been created:
    diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt
    index 74085cd3308..036f8968511 100644
    --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt
    +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-acr.txt
    @@ -12,7 +12,7 @@ Closely examine the JSON output from this command. Save the value of the `loginS
     "loginServer": "contosoresourcegroup1610068510.azurecr.io",
     ```
     
    -Use this value to sign in to the ACR instance. Note that because you are signing in with the `az` cli, you do not need a password because your identity is already conveyed by having done `az login` previously.
    +Use this value to sign in to the ACR instance. Note that because you are signing in with the `az` CLI, you do not need a password because your identity is already conveyed by having done `az login` previously.
     
     ```shell
     $ export LOGIN_SERVER=$(az acr show -n $ACR_NAME --resource-group $AKS_PERS_RESOURCE_GROUP --query "loginServer" -o tsv)
    diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt
    index e5fbcee33b2..d0c1a75e184 100644
    --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt
    +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt
    @@ -1,9 +1,11 @@
     ##### Oracle Container Registry
     
    -You will need an Oracle account. The following steps will direct you to accept the license agreement for WebLogic Server.  Make note of your Oracle Account password and email.  This sample pertains to 12.2.1.4, but other versions may work as well.
    +The following steps will direct you to accept the license agreement for WebLogic Server. Make note of your Oracle Account password and email. This sample pertains to 12.2.1.4, but other versions may work as well.
     
       - In a web browser, navigate to https://container-registry.oracle.com and log in using the Oracle Single Sign-On authentication service. If you do not already have SSO credentials, at the top of the page, click the **Sign In** link to create them.
       - The Oracle Container Registry provides a WebLogic 12.2.1.4 General Availability (GA) installation image that is used in this sample.
    +    - In the Oracle Container Registry, navigate to **Middleware**, then **weblogic**.
    +    - On the left, choose a language and accept the license agreement. You will then see a message such as: "You last accepted the Oracle Standard Terms and Restrictions on 08/10/2020 at 06:12 AM Coordinated Universal Time (UTC)."
       - **NOTE**: General Availability (GA) images are suitable for demonstration and development purposes _only_ where the environments are not available from the public Internet; they are **not
          acceptable for production use**. In production, you should always use CPU (patched) images
          from the OCR
    @@ -11,7 +13,7 @@ You will need an Oracle account. The following steps will direct you to accept t
          (WIT) with the `--recommendedPatches` option. For more guidance,
          see [Apply the Latest Patches and Updates](https://www.oracle.com/pls/topic/lookup?ctx=en/middleware/standalone/weblogic-server/14.1.1.0&id=LOCKD-GUID-2DA84185-46BA-4D7A-80D2-9D577A4E8DE2)
          in _Securing a Production Environment for Oracle WebLogic Server_.
    -  - Ensure that Docker is running.  Find and then pull the WebLogic 12.2.1.4 installation image:
    +  - Ensure that Docker is running. Find and pull the WebLogic 12.2.1.4 installation image:
          ```shell
          $ docker login container-registry.oracle.com -u ${ORACLE_SSO_EMAIL} -p ${ORACLE_SSO_PASSWORD}
          $ docker pull container-registry.oracle.com/middleware/weblogic:12.2.1.4
    diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt
    index afbc1d14fe2..efc2a106bc4 100644
    --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt
    +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt
    @@ -3,7 +3,7 @@
     
     This sample doesn't enable application routing. If you want to enable application routing, follow [Managed nginx Ingress with the application routing add-on in AKS](https://learn.microsoft.com/azure/aks/app-routing?tabs=default%2Cdeploy-app-default).
     
    -Run the following commands to create the AKS cluster instance.
    +Run the following command to create the AKS cluster.
     
     ```shell
     $ az aks create \
    diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt
    index 3af942c79b9..6d56823506c 100644
    --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt
    +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt
    @@ -61,21 +61,21 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
     
     3. Assign the AKS cluster **Contributor** role to access the storage account.
     
    -    You must configure role assignment allowing access from AKS cluster to the storage account.
    +    You must configure role assignment allowing access from the AKS cluster to the storage account.
     
    -    Get `objectId` of the AKS cluster with the following command and save it with variable `AKS_OBJECT_ID`:
    +    Get the `objectId` of the AKS cluster with the following command and save it with the variable `AKS_OBJECT_ID`:
     
         ```shell
         $ AKS_OBJECT_ID=$(az aks show --name ${AKS_CLUSTER_NAME} --resource-group ${AKS_PERS_RESOURCE_GROUP} --query "identity.principalId" -o tsv)
         ```
     
    -    Get Id of the storage account with the following command:
    +    Get the Id of the storage account with the following command:
     
         ```shell
         $ STORAGE_ACCOUNT_ID=$(az storage account show --name ${AKS_PERS_STORAGE_ACCOUNT_NAME} --resource-group ${AKS_PERS_RESOURCE_GROUP} --query "id" -o tsv)
         ```
     
    -    Now, you are able to create a role assignment to grant the AKS cluster **Contributor** in the scope of the storage account. Then, the AKS cluster is able to access the file share.
    +    Now, you are able to create a role assignment to grant the AKS cluster the **Contributor** role in the scope of the storage account. Then, the AKS cluster is able to access the file share.
     
         ```shell
         $ az role assignment create \
    @@ -85,7 +85,7 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
           --scope "${STORAGE_ACCOUNT_ID}"
         ```
     
    -    Successful output will be a JSON object with string like:
    +    Successful output will be a JSON object like the following:
     
         ```json
         {
    @@ -112,7 +112,7 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
     
     4. Configure network security.
     
    -    You must configure the network security allowing access from AKS cluster to the file share.
    +    You must configure network security allowing access from the AKS cluster to the file share.
     
         First, you must get the virtual network name and the subnet name of the AKS cluster.
     
    @@ -149,7 +149,7 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
             --service-endpoints Microsoft.Storage
         ```
     
    -    It takes several minutes to enable the service endpoint; successful output will be a JSON object with string like:
    +    It takes several minutes to enable the service endpoint; successful output will be a JSON object like the following:
     
         ```text
         "serviceEndpoints": [
    @@ -163,8 +163,8 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
         }
         ```
     
    -    Now you must create a network rule to allow access from AKS cluster.
    -    The following command enables access from AKS subnet to the storage account:
    +    Now you must create a network rule to allow access from the AKS cluster.
    +    The following command enables access from the AKS subnet to the storage account:
     
         ```shell
         $ az storage account network-rule add \
    @@ -173,7 +173,7 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
           --subnet ${aksSubnetId}
         ```
     
    -    Successful output will be a JSON object with virtual network rule like:
    +    Successful output will be a JSON object with a virtual network rule like:
     
         ```text
         "virtualNetworkRules": [
    @@ -188,7 +188,7 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha
     ##### Create SC and PVC
     
     ##### Generated configuration files
    -Use the below command to generate configuration files.
    +Use the following command to generate configuration files.
     
     ```shell
     cat >azure-csi-nfs-${TIMESTAMP}.yaml <}}
     
    -A Model in Image image can contain multiple properties files, archive ZIP files, and YAML files but in this sample you use just one of each. For a complete description of Model in Images model file naming conventions, file loading order, and macro syntax, see [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}) files in the Model in Image user documentation.
    +A Model in Image image can contain multiple properties files, archive ZIP files, and YAML files but in this sample you use just one of each. For a complete description of Model in Images model file naming conventions, file loading order, and macro syntax, see [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}) in the Model in Image user documentation.
     
     ##### Creating the image with WIT
     
    @@ -241,6 +258,9 @@ $ docker tag wdt-domain-image:WLS-v1 $LOGIN_SERVER/mii-aks-auxiliary-image:1.0
     ```shell
     $ docker push $LOGIN_SERVER/mii-aks-auxiliary-image:1.0
     ```
    +
    +The output will show something similar to the following:
    +
     ```
     The push refers to repository [contosorgresourcegroup1610068510.azurecr.io/mii-aks-auxiliary-image]
     1.0: digest: sha256:208217afe336053e4c524caeea1a415ccc9cc73b206ee58175d0acc5a3eeddd9 size: 2415
    @@ -295,6 +315,9 @@ $ $BASE_DIR/sample-scripts/create-kubernetes-secrets/create-docker-credentials-s
       -p ${ORACLE_SSO_PASSWORD} \
       -u ${ORACLE_SSO_EMAIL}
     ```
    +
    +The output will show something similar to the following:
    +
     ```
     secret/wlsregcred created
     The secret wlsregcred has been successfully created in the sample-domain1-ns namespace.
    @@ -376,7 +399,7 @@ wlsregcred                                 kubernetes.io/dockerconfigjson   1
     
     Now, you create a domain YAML file. Think of the domain YAML file as the way to configure some aspects of your WebLogic domain using Kubernetes.  The operator uses the Kubernetes "custom resource" feature to define a Kubernetes resource type called `Domain`.  For more on the `Domain` Kubernetes resource, see [Domain Resource]({{< relref "/managing-domains/domain-resource" >}}). For more on custom resources see [the Kubernetes documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/).
     
    -We provide a script at `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh` to generate domain resource description.
    +We provide a script at `$BASE_DIR/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-mii-generate-yaml.sh` to generate a domain resource description.
     
     Run the following command to generate resource files.
     
    @@ -453,9 +476,9 @@ If the system does not reach this state, troubleshoot and resolve the problem be
     
     ##### Create Azure load balancer
     
    -Create the Azure public standard load balancer to access the WebLogic Server Administration Console and applications deployed in the cluster.
    +Create an Azure public standard load balancer to access the WebLogic Server Administration Console and applications deployed to the cluster.
     
    -Use the configuration file in `admin-lb.yaml` to create a load balancer service for the Administration Server. If you are choosing not to use the predefined YAML file and instead created a new one with customized values, then substitute the following content with you domain values.
    +Use the file `admin-lb.yaml` to create a load balancer service for the Administration Server. If you are choosing not to use the predefined YAML file and instead created a new one with customized values, then substitute the following content with your domain values.
     
     {{%expand "Click here to view YAML content." %}}
     ```yaml
    @@ -478,7 +501,7 @@ spec:
     ```
     {{% /expand %}}
     
    -Use the configuration file in `cluster-lb.yaml` to create a load balancer service for the managed servers. If you are choosing not to use the predefined YAML file and instead created new one with customized values, then substitute the following content with you domain values.
    +Use the file `cluster-lb.yaml` to create a load balancer service for the managed servers. If you are choosing not to use the predefined YAML file and instead created new one with customized values, then substitute the following content with your domain values.
     
     {{%expand "Click here to view YAML content." %}}
     ```yaml
    @@ -502,17 +525,24 @@ spec:
     ```
     {{% /expand %}}
     
    -Create the load balancer services using the following command:
    +Create the load balancer services using the following commands:
     
     ```shell
     $ kubectl apply -f admin-lb.yaml
     ```
    +
    +Successful output will look like:
    +
     ```
     service/sample-domain1-admin-server-external-lb created
     ```
    +
     ```shell
     $ kubectl  apply -f cluster-lb.yaml
     ```
    +
    +Successful output will look like:
    +
     ```
     service/sample-domain1-cluster-1-external-lb created
     ```
    @@ -522,6 +552,9 @@ Get the external IP addresses of the Administration Server and cluster load bala
     ```shell
     $ kubectl get svc -n sample-domain1-ns --watch
     ```
    +
    +Successful output will look like:
    +
     ```
     NAME                                      TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)          AGE
     sample-domain1-admin-server               ClusterIP      None                      7001/TCP         8m33s
    @@ -545,7 +578,7 @@ $ kubectl describe domain domain1
     
     Make sure the status of cluster-1 is `ServersReady` and `Available`.
     
    -{{%expand "Click here to view the example domain status." %}}
    +{{%expand "Click here to view example domain status." %}}
     ```yaml
     Name:         sample-domain1
     Namespace:    sample-domain1-ns
    @@ -689,6 +722,8 @@ $ CLUSTER_IP=$(kubectl -n sample-domain1-ns get svc sample-domain1-cluster-1-lb
     $ curl http://${CLUSTER_IP}:8001/myapp_war/index.jsp
     ```
     
    +Successful output will look like:
    +
     ```
     
     *****************************************************************
    diff --git a/documentation/site/static/images/aks-solution.png b/documentation/site/static/images/aks-solution.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..77455ab3a6e8539617a2cd3e53c357cb9371185e
    GIT binary patch
    literal 88692
    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

    uXr|jzs#YpsV!iIm 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 KWD2F;e*Ry=!ObiH literal 0 HcmV?d00001 diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh index 5aebf3a7350..43832519789 100644 --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh +++ b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks-inputs.sh @@ -4,38 +4,10 @@ # The version of this inputs file. Do not modify. export version=create-domain-on-aks-inputs-v1 -# -# Parameters that must be changed from these values! -# - -# Oracle Single Sign-On (SSO) account email, used to pull the WebLogic Server image. -export dockerEmail=docker-email - -# Oracle SSO account password, used to pull the WebLogic Server image. -export dockerPassword=docker-password - -# Name of weblogic user account. -export weblogicUserName=weblogic - -# Password for WebLogic user account. -export weblogicAccountPassword=Secret123456 - -# Specify where to create azure resource. -export azureLocation=eastus - -# Specify a prefix to name resources, only allow lowercase letters and numbers, between 1 and 7 characters. -# Resource group is named with ${namePrefix}resourcegroup, e.g. wlsresourcegroup1592469388 -# Kubernetes cluster is named with ${namePrefix}akscluster, e.g. wlsakscluster1592469388 -# Storage account is named with ${namePrefix}storage, e.g. wlsstorage1592469388 -export namePrefix=wls - # # Parameters that may optionally be changed. # -# The suffix of file share secret name, the complete value is ${namePrefix}${azureFileShareSecretNameSuffix}. -export azureFileShareSecretNameSuffix=azure-secret - # Number of azure kubernetes nodes, used to create azure kubernetes cluster. export azureKubernetesNodeCount=2 @@ -48,17 +20,6 @@ export azureKubernetesNodepoolNamePrefix=pool1 #Java Option for WebLogic Server export javaOptions="-Dweblogic.StdoutDebugEnabled=false -XX:InitialRAMPercentage=25.0 -XX:MaxRAMPercentage=50.0" -# The suffix of the Kubernetes secret name, the complete value is ${namePrefix}${imagePullSecretNameSuffix}. The secret name is used to access the container registry to pull the WebLogic Server image -# Used to create Kubernetes secret for container registry account. -# Parameter "imagePullSecretName" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml -export imagePullSecretNameSuffix=regcred - -# Storage class name for Azure Files using Container Storage Interface driver, see https://docs.microsoft.com/en-us/azure/aks/azure-files-csi#nfs-file-shares -export azureFileCsiNfsClassName=azurefile-csi-nfs - -# The suffix of azure storage file share name, the complete value is ${namePrefix}-${azureStorageShareNameSuffix}-, used to create file share, and mount file share. -export azureStorageShareNameSuffix=weblogic - # Resource request for each server pod (Memory and CPU). This is minimum amount of compute # resources required for each server pod. Edit value(s) below as per pod sizing requirements. # These are optional @@ -77,10 +38,6 @@ export serverPodCpuRequest="250m" export serverPodMemoryLimit="1.5Gi" export serverPodCpuLimit="250m" -# The suffix of the persistent volume claim name, the complete value is ${namePrefix}-${persistentVolumeClaimNameSuffix}-. -# Parameter "persistentVolumeClaimName" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml -export persistentVolumeClaimNameSuffix=azurefile - # WebLogic Server image. # Parameter "image" will be overwritten with this field in kubernetes/samples/scripts/create-weblogic-domain/domain-home-on-pv/create-domain-inputs.yaml # **NOTE**: diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh index 0a19b37bb63..d6e8f07c4ef 100755 --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh +++ b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh @@ -29,6 +29,7 @@ scriptDir="$(cd "$(dirname "${script}")" && pwd)" #Kubernetes command line interface. #Default is 'kubectl' if KUBERNETES_CLI env variable is not set. kubernetesCli=${KUBERNETES_CLI:-kubectl} +azureResourceUID=${TIMESTAMP} if [ -z "${azureResourceUID}" ]; then azureResourceUID=$(date +%s) @@ -46,30 +47,42 @@ fail() { BLUE="\033[34m" RED="\033[31m" RESET="\033[0m" +YELLOW="\033[33m" +GREEN="\033[32m" # Function: Print colored message print_message() { - local contenxt="$1" - local color="$2" + local contenxt="$1" + local color="$2" - echo -e "${color} ${contenxt}${RESET}" + echo -e "${color} ${contenxt}${RESET}" } print_blue() { - local contenxt="$1" - echo -e "${BLUE} ${contenxt}${RESET}" + local contenxt="$1" + echo -e "${BLUE} ${contenxt}${RESET}" } print_red() { - local contenxt="$1" - echo -e "${RED} ${contenxt}${RESET}" + local contenxt="$1" + echo -e "${RED} ${contenxt}${RESET}" +} + +print_yellow() { + local contenxt="$1" + echo -e "${YELLOW} ${contenxt}${RESET}" +} + +print_green() { + local contenxt="$1" + echo -e "${GREEN} ${contenxt}${RESET}" } steps=0 -total_steps=11 +total_steps=12 print_step() { - ((steps++)) - print_blue "Progress $steps/$total_steps.......... $1" + ((steps++)) + print_blue "Progress $steps/$total_steps.......... $1" } # # Function to validate the host environment meets the prerequisites. @@ -89,102 +102,222 @@ envValidate() { # "Checking if Java is installed..." if type -p java; then - print_blue "Java JDK is installed. Version:" - java -version + print_blue "Java JDK is installed. Version:" + java -version else - print_red "[ERROR]Java JDK is not installed. Please install Java JDK." - exit 1 + print_red "[ERROR]Java JDK is not installed. Please install Java JDK." + exit 1 fi # Check if Docker is installed - if command -v docker &> /dev/null; then - echo "Docker is installed." + if command -v docker &>/dev/null; then + echo "Docker is installed." else - print_red "[ERROR]Docker is not installed. Please install Docker." - exit 1 + print_red "[ERROR]Docker is not installed. Please install Docker." + exit 1 fi # Check if Helm is installed - if command -v helm &> /dev/null; then - print_blue "Helm is installed." + if command -v helm &>/dev/null; then + print_blue "Helm is installed." else - print_red "[ERROR]Helm is not installed. Please install Helm." - exit 1 + print_red "[ERROR]Helm is not installed. Please install Helm." + exit 1 fi # Check if kubectl is installed - if command -v ${kubernetesCli} &> /dev/null; then - print_blue "${kubernetesCli} is installed." + if command -v ${kubernetesCli} &>/dev/null; then + print_blue "${kubernetesCli} is installed." else - print_red "[ERROR]${kubernetesCli} is not installed. Please install ${kubernetesCli}." - exit 1 + print_red "[ERROR]${kubernetesCli} is not installed. Please install ${kubernetesCli}." + exit 1 fi echo "Checking host environment passed." } +# +# Function to prompt the user for input +# $1 - parameter name +# $2 - parameter type +# $3 - parameter length +# $4 - default value +inputParameter() { + local paramName="$1" + local parmType="$2" + local length="$3" + local defaultValue="$4" + + print_yellow "Please provide a value for ${paramName}." + read -p "Enter ${parmType}: " input_string + + if [ -z "${input_string}" ]; then + input_string=${defaultValue} + fi + + if [[ "$input_string" =~ ^[A-Za-z0-9]+$ ]] && [[ ${#input_string} -le ${length} ]]; then + export inputValue=$input_string + else + echo "Invalid input. Please enter a valid value for ${paramName}." + exit 1 + fi +} + +# +# Function to prompt the user for password input +# $1 - parameter name +inputPassword() { + local paramName="$1" + + print_yellow "Please provide a value for ${paramName}." + read -sp "Please enter password: " password + echo + read -sp "Please confirm your password: " password_confirm + echo + + # Check if the passwords match + if [ "$password" == "$password_confirm" ]; then + echo "Password is set successfully." + export inputValue=$password + else + echo "Passwords do not match. Please try again." + exit 1 + fi +} + +# +# Function to prompt the user for email input +# $1 - parameter name +inputEmail() { + local paramName="$1" + + print_yellow "Please provide a value for ${paramName}." + read -p "Enter Email: " input_string + + if [ -n "${input_string}" ]; then + export inputValue=$input_string + else + echo "Invalid input. Please enter a valid value for ${paramName}." + exit 1 + fi +} + +# +# Function to print the parameters +# +print_parameters() { + print_green "image_build_branch_name=${image_build_branch_name}" + print_green "image_build_base_dir=${image_build_base_dir}" + print_green "dockerEmail=${dockerEmail}" + print_green "weblogicUserName=${weblogicUserName}" + + print_green "namePrefix=${namePrefix}" + print_green "azureLocation=${azureLocation}" + print_green "azureResourceGroupName=${azureResourceGroupName}" + print_green "aksClusterName=${aksClusterName}" + print_green "storageAccountName=${storageAccountName}" + print_green "acrName=${acrName}" + print_green "azureKubernetesNodepoolName=${azureKubernetesNodepoolName}" + print_green "azureStorageShareName=${azureStorageShareName}" + print_green "oracleSsoK8sSecretName=${oracleSsoK8sSecretName}" + print_green "domainUID=${domainUID}" + print_green "sampleScriptsDir=${sampleScriptsDir}" +} + parametersValidate() { print_step "validating parameters" - # Get the values of environment variables - email="$dockerEmail" - password="$dockerPassword" - - while getopts "u:p:" option; do - case "${option}" in - u) - email=${OPTARG} - ;; - p) - password=${OPTARG} - ;; - esac - done + local stringFormatLength5="string contains both letters and numbers, between 1 and 5 characters" + local stringFormatLength12="string contains both letters and numbers, between 1 and 12 characters" + local stringFormatLength20="string contains both letters and numbers, between 1 and 20 characters" - # Check for default values and prompt for setting - if [ "$email" = "docker-email" ]; then - echo -n "Please enter a value for 'dockerEmail'(Oracle Single Sign-On (SSO) account email): " - read input_email - if [ -z "$input_email" ]; then - echo "No value provided for 'dockerEmail'. Please set the value and rerun the script." - exit 1 - fi - email="$input_email" + if [ -z "${namePrefix}" ]; then + inputParameter "a prefix to name resources" "${stringFormatLength5} [default=wls]" 5 "wls" + export namePrefix=$inputValue fi - if [ "$password" = "docker-password" ]; then - echo -n "Please enter a value for 'dockerPassword'(Oracle Single Sign-On (SSO) account password): " - read -s input_password - echo - if [ -z "$input_password" ]; then - echo "No value provided for 'dockerPassword'. Please set the value and rerun the script." - exit 1 - fi - password="$input_password" + if [ -z "${azureLocation}" ]; then + inputParameter "Azure location" "Azure location [default=eastus]" 12 "eastus" + export azureLocation=$inputValue + fi + + if [ -z "${acrName}" ]; then + inputParameter "Azure Container Registry name" "${stringFormatLength20} [default=${namePrefix}acr${azureResourceUID}]" 20 "${namePrefix}acr${azureResourceUID}" + export acrName=$inputValue + fi + + if [ -z "${oracleSsoK8sSecretName}" ]; then + inputParameter "the Kubernetes secret name associated with the Oracle SSO account." "${stringFormatLength12} [default=${namePrefix}regcred]" 12 "${namePrefix}regcred" + export oracleSsoK8sSecretName=$inputValue + fi + + if [ -z "${azureResourceGroupName}" ]; then + inputParameter "resource group name" "${stringFormatLength20} [default=${namePrefix}rg${azureResourceUID}]" 20 "${namePrefix}rg${azureResourceUID}" + export azureResourceGroupName=$inputValue + fi + + if [ -z "${aksClusterName}" ]; then + inputParameter "Azure Kubernetes Service name" "${stringFormatLength20} [default=${namePrefix}aks${azureResourceUID}]" 20 "${namePrefix}aks${azureResourceUID}" + export aksClusterName=$inputValue fi - # Export the updated values of environment variables - export dockerEmail="$email" - export dockerPassword="$password" + if [ -z "${storageAccountName}" ]; then + inputParameter "Azure Storage Account name" "${stringFormatLength20} [default=${namePrefix}stg${azureResourceUID}]" 20 "${namePrefix}stg${azureResourceUID}" + export storageAccountName=$inputValue + fi + + if [ -z "${azureStorageShareName}" ]; then + inputParameter "Azure Storage File Share name" "${stringFormatLength20} [default=${namePrefix}${azureResourceUID}]" 20 "${namePrefix}${azureResourceUID}" + export azureStorageShareName=$inputValue + fi + + if [ -z "${domainUID}" ]; then + inputParameter "WebLogic Domain UID" "${stringFormatLength12} [default=domain1]" 12 "domain1" + export domainUID=$inputValue + fi + + if [ -z "${dockerEmail}" ]; then + inputEmail "Oracle Single Sign-On (SSO) account email" + export dockerEmail=$inputValue + fi + + if [ -z "${dockerPassword}" ]; then + inputPassword "Oracle Single Sign-On (SSO) account password" + export dockerPassword=$inputValue + fi + + if [ -z "${weblogicUserName}" ]; then + inputParameter "Name of weblogic user account" "${stringFormatLength12} [default=weblogic]" 12 "weblogic" + export weblogicUserName=$inputValue + fi + + if [ -z "${weblogicAccountPassword}" ]; then + inputPassword "Password of weblogic user account" + export weblogicAccountPassword=$inputValue + fi + ssoAccountValidate + print_parameters +} + +ssoAccountValidate() { + print_step "Validate Oracle SSO Account. Make sure docker is running." # Attempt to login to Docker sudo chmod 666 /var/run/docker.sock - docker login container-registry.oracle.com -u "$dockerEmail" -p "$dockerPassword" > /dev/null 2>&1 + docker login container-registry.oracle.com -u "$dockerEmail" -p "$dockerPassword" >/dev/null 2>&1 # Check the login result if [ $? -eq 0 ]; then echo "Oracle Single Sign-On (SSO) account Username and password are correct" # Logout from Docker - docker logout > /dev/null 2>&1 + docker logout >/dev/null 2>&1 else print_red "[ERROR]Invalid Oracle Single Sign-On (SSO) account username or password." exit 1 fi - } - # # Function to setup the environment to run the create Azure resource and domain job # @@ -196,36 +329,34 @@ initialize() { source ./create-domain-on-aks-inputs.sh source ~/.bashrc - + # Generate Azure resource name - export image_build_branch_name="v4.2.5" + export image_build_branch_name="v4.2.8" export image_build_base_dir="/tmp/tmp${azureResourceUID}" - export acr_account_name=${namePrefix}acr${azureResourceUID} - export docker_secret_name="${namePrefix}regcred" + export dockerEmail=${ORACLE_SSO_EMAIL} + export dockerPassword=${ORACLE_SSO_PASSWORD} + export weblogicUserName=${WEBLOGIC_USERNAME} + export weblogicAccountPassword=${WEBLOGIC_PASSWORD} - export azureResourceGroupName="${namePrefix}resourcegroup${azureResourceUID}" - export aksClusterName="${namePrefix}akscluster${azureResourceUID}" - export storageAccountName="${namePrefix}storage${azureResourceUID}" + export namePrefix=${NAME_PREFIX} + export azureLocation=${AKS_PERS_LOCATION} + export acrName=${ACR_NAME} + export oracleSsoK8sSecretName="${SECRET_NAME_DOCKER}" - export azureKubernetesNodepoolName="${azureKubernetesNodepoolNamePrefix}${namePrefix}" - export azureStorageShareName="${namePrefix}-${azureStorageShareNameSuffix}-${azureResourceUID}" - export domainUID="domain1" + export azureResourceGroupName="${AKS_PERS_RESOURCE_GROUP}" + export aksClusterName="${AKS_CLUSTER_NAME}" + export storageAccountName="${AKS_PERS_STORAGE_ACCOUNT_NAME}" + export azureKubernetesNodepoolName="${azureKubernetesNodepoolNamePrefix}${namePrefix}" + export azureStorageShareName="${AKS_PERS_SHARE_NAME}" + export domainUID="${domainUID}" + export sampleScriptsDir=${BASE_DIR}/sample-scripts - echo "image_build_branch_name=${image_build_branch_name}" - echo "aksClusterName=${aksClusterName}" - echo "storageAccountName=${storageAccountName}" - - echo "azureResourceGroupName=${azureResourceGroupName}" - echo "image_build_base_dir=${image_build_base_dir}" - echo "acr_account_name=${acr_account_name}" - - + print_parameters } - createResourceGroup() { print_step "createing resourcegroup" @@ -271,24 +402,24 @@ createAndConnectToAKSCluster() { retry_count=0 while true; do - # Execute create AKS command - $create_command - - # Check exit status - if [ $? -eq 0 ]; then - echo "AKS creation successful" - break + # Execute create AKS command + $create_command + + # Check exit status + if [ $? -eq 0 ]; then + echo "AKS creation successful" + break + else + retry_count=$((retry_count + 1)) + if [ $retry_count -le $max_retries ]; then + echo "AKS creation failed. Retrying attempt $retry_count..." + # Delete previously created AKS + az aks delete --resource-group $azureResourceGroupName --name $aksClusterName --yes --no-wait else - retry_count=$((retry_count+1)) - if [ $retry_count -le $max_retries ]; then - echo "AKS creation failed. Retrying attempt $retry_count..." - # Delete previously created AKS - az aks delete --resource-group $azureResourceGroupName --name $aksClusterName --yes --no-wait - else - echo "Maximum retry limit reached. Unable to create AKS" - exit 1 - fi + echo "Maximum retry limit reached. Unable to create AKS" + exit 1 fi + fi done # Connect to AKS cluster @@ -310,28 +441,28 @@ createFileShare() { echo Creating Azure Storage Account ${storageAccountName}. az storage account create \ - -n $storageAccountName \ - -g $azureResourceGroupName \ - -l $azureLocation \ - --sku Premium_LRS \ - --kind FileStorage \ - --https-only false \ - --default-action Deny + -n $storageAccountName \ + -g $azureResourceGroupName \ + -l $azureLocation \ + --sku Premium_LRS \ + --kind FileStorage \ + --https-only false \ + --default-action Deny echo Creating Azure NFS file share. az storage share-rm create \ - --resource-group $azureResourceGroupName \ - --storage-account $storageAccountName \ - --name ${azureStorageShareName} \ - --enabled-protocol NFS \ - --root-squash NoRootSquash \ - --quota 100 + --resource-group $azureResourceGroupName \ + --storage-account $storageAccountName \ + --name ${azureStorageShareName} \ + --enabled-protocol NFS \ + --root-squash NoRootSquash \ + --quota 100 configureStorageAccountNetwork # Echo storage account name and key echo Storage account name: $storageAccountName - echo NFS file share name: ${azureStorageShareName} + echo NFS file share name: ${azureStorageShareName} } @@ -340,10 +471,10 @@ configureStorageAccountNetwork() { local storageAccountId=$(az storage account show --name ${storageAccountName} --resource-group ${azureResourceGroupName} --query "id" -o tsv) az role assignment create \ - --assignee-object-id "${aksObjectId}" \ - --assignee-principal-type "ServicePrincipal" \ - --role "Contributor" \ - --scope "${storageAccountId}" + --assignee-object-id "${aksObjectId}" \ + --assignee-principal-type "ServicePrincipal" \ + --role "Contributor" \ + --scope "${storageAccountId}" if [ $? != 0 ]; then fail "Failed to grant the AKS cluster with Contibutor role to access the storage account." @@ -357,7 +488,7 @@ configureStorageAccountNetwork() { local aksNetworkName=$(az graph query -q "Resources \ | where type =~ 'Microsoft.Network/virtualNetworks' \ | where resourceGroup =~ '${aksMCRGName}' \ - | project name = name" --query "data[0].name" -o tsv) + | project name = name" --query "data[0].name" -o tsv) echo "aksNetworkName="${aksNetworkName} @@ -402,14 +533,11 @@ createWebLogicDomain() { echo Creating WebLogic Server domain ${domainUID} # create credentials - cd ${image_build_base_dir} - cd weblogic-kubernetes-operator/kubernetes/samples/scripts/create-weblogic-domain-credentials + cd ${sampleScriptsDir}/create-weblogic-domain-credentials ./create-weblogic-credentials.sh -u ${weblogicUserName} -p ${weblogicAccountPassword} -d ${domainUID} - cd ${image_build_base_dir} - cd weblogic-kubernetes-operator/kubernetes/samples/scripts/create-kubernetes-secrets - - ./create-docker-credentials-secret.sh -s ${docker_secret_name} -e ${dockerEmail} -p ${dockerPassword} -u ${dockerEmail} + cd ${sampleScriptsDir}/create-kubernetes-secrets + ./create-docker-credentials-secret.sh -s ${oracleSsoK8sSecretName} -e ${dockerEmail} -p ${dockerPassword} -u ${dockerEmail} # generate yaml generateYamls @@ -426,9 +554,9 @@ createWebLogicDomain() { } generateYamls() { - -echo "generating yamls..." -cat >azure-csi-nfs.yaml <azure-csi-nfs.yaml <pvc.yaml <pvc.yaml <domain-resource.yaml <domain-resource.yaml <admin-lb.yaml <admin-lb.yaml <cluster-lb.yaml <cluster-lb.yaml < Date: Wed, 30 Oct 2024 13:27:37 -0400 Subject: [PATCH 209/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 4dd6ab98e8a..77bf89cf605 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -30,7 +30,7 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.15.0 + 3.15.1 org.junit.jupiter diff --git a/pom.xml b/pom.xml index da2be2c4c02..645ccaaf8cc 100644 --- a/pom.xml +++ b/pom.xml @@ -693,9 +693,9 @@ 3.8.0 3.6.0 3.5.0 - 10.18.2 + 10.19.0 1.0 - 3.5.0 + 3.6.0 3.2.7 2.0.0.0 1.3.3 @@ -731,13 +731,13 @@ 4.0.2 6.1.0 0.16.0 - 2.18.0 + 2.18.1 2.18.0 2.3 2.11.0 11.0.0 2.0.16 - 1.5.11 + 1.5.12 4.28.3 2.5.1 9.41.2 From edf50fd78e4a04c3866741ea5f5a8b01ba1dde69 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 31 Oct 2024 09:08:56 -0400 Subject: [PATCH 210/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 2 +- pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 77bf89cf605..995893bfa2e 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -59,7 +59,7 @@ org.apache.maven.plugins maven-plugin-plugin - 3.15.0 + 3.15.1 oracle.kubernetes:jsonschema-maven-plugin diff --git a/pom.xml b/pom.xml index 645ccaaf8cc..3587596ee18 100644 --- a/pom.xml +++ b/pom.xml @@ -703,7 +703,7 @@ 2.0.1 1.0.39 1.9.0 - 1.5.2 + 1.5.3 1.4.0 1.17.1 1.7.3 @@ -720,7 +720,7 @@ 2.0.21 4.12.0 3.9.1 - 1.78.1 + 1.79 5.11.3 5.7.1 1.7.0 @@ -732,7 +732,7 @@ 6.1.0 0.16.0 2.18.1 - 2.18.0 + 2.18.1 2.3 2.11.0 11.0.0 From 9b14872c21956d71c301a921029f885c43b067a4 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Thu, 31 Oct 2024 13:18:24 +0000 Subject: [PATCH 211/356] Fix for secure domain upgrade nightly failures --- .../kubernetes/ItMiiDomainUpgradeToSecureMode.java | 4 ++-- .../resources/apps/sample-app/sample-war/index.jsp | 12 ++++++++++++ .../securemodeupgrade/upgrade-startmode-prod.yaml | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java index 8c896dbac65..fea374901c3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDomainUpgradeToSecureMode.java @@ -135,7 +135,7 @@ class ItMiiDomainUpgradeToSecureMode { private final String imageTag12214 = "12.2.1.4"; private final String imageTag1412 = "14.1.2.0.0-jdk17"; private final String image1412 = BASE_IMAGES_PREFIX + WEBLOGIC_IMAGE_NAME_DEFAULT + ":" + imageTag1412; - private final String sampleAppUri = "/sample-war/index.jsp"; + private final String sampleAppUri = "/sample-war/index.jsp?terminateSession=true"; private final String adminAppUri = "/management/tenant-monitoring/servers"; private final String adminAppText = "RUNNING"; private final String adminAppMoved = "This document you requested has moved"; @@ -1020,7 +1020,7 @@ void testUpgrade12214to1412ServerStartModeProd() throws UnknownHostException { "running in production mode"); //create ingress resources to route traffic to various service endpoints - createNginxIngressHostRouting(domainUid, 7001, 7002, 7100, nginxParams.getIngressClassName(), false); + createNginxIngressHostRouting(domainUid, 7001, 7002, 11000, nginxParams.getIngressClassName(), false); //verify the number of channels available in the domain resource match with the count and name verifyChannel(domainNamespace, domainUid, List.of(channelName)); diff --git a/integration-tests/src/test/resources/apps/sample-app/sample-war/index.jsp b/integration-tests/src/test/resources/apps/sample-app/sample-war/index.jsp index 4c57573d754..3464af53455 100644 --- a/integration-tests/src/test/resources/apps/sample-app/sample-war/index.jsp +++ b/integration-tests/src/test/resources/apps/sample-app/sample-war/index.jsp @@ -4,4 +4,16 @@ Licensed under the Universal Permissive License v 1.0 as shown at https://oss.or --%> <% out.println("Hello World, you have reached server " + System.getProperty("weblogic.Name" )); + // Check if the "terminateSession" parameter is set to "true" + String terminateSession = request.getParameter("terminateSession"); + if ("true".equalsIgnoreCase(terminateSession) && session != null) { + // Print session info + out.println("Session ID: " + session.getId() + "
    "); + out.println("Creation Time: " + new java.util.Date(session.getCreationTime()) + "
    "); + out.println("Last Accessed Time: " + new java.util.Date(session.getLastAccessedTime()) + "
    "); + session.invalidate(); + out.println("Session has been terminated."); + } else { + out.println("No active session."); + } %> diff --git a/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml b/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml index 4ca2c37a9a4..1578a163017 100644 --- a/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml +++ b/integration-tests/src/test/resources/securemodeupgrade/upgrade-startmode-prod.yaml @@ -11,7 +11,7 @@ topology: ServerTemplate: myserver-template: Cluster: mycluster - ListenPort: 7100 + ListenPort: 11000 Cluster: mycluster: DynamicServers: From 61874435242fc3ee4c9f80ebd81e17a4b4236ebd Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 31 Oct 2024 10:25:34 -0400 Subject: [PATCH 212/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3587596ee18..533f9c14fd4 100644 --- a/pom.xml +++ b/pom.xml @@ -689,8 +689,8 @@ 3.5.0 3.1.1 3.10.1 - 3.5.0 - 3.8.0 + 3.5.1 + 3.8.1 3.6.0 3.5.0 10.19.0 From bc77b4e4a90b402907d0d07b061e0a073c7811c7 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 1 Nov 2024 13:36:40 -0400 Subject: [PATCH 213/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 533f9c14fd4..091710fafef 100644 --- a/pom.xml +++ b/pom.xml @@ -686,14 +686,14 @@ 3.3.1 3.21.0 3.5.1 - 3.5.0 + 3.6.0 3.1.1 3.10.1 3.5.1 3.8.1 3.6.0 3.5.0 - 10.19.0 + 10.20.0 1.0 3.6.0 3.2.7 @@ -735,12 +735,12 @@ 2.18.1 2.3 2.11.0 - 11.0.0 + 11.1.0 2.0.16 1.5.12 4.28.3 2.5.1 - 9.41.2 + 9.44 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 4821d7f3a55aaeb55f06b41bbad3f27237370097 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 1 Nov 2024 14:04:36 -0400 Subject: [PATCH 214/356] Prepare for release --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 04ffb0a31dd..96d60160ea3 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 1a4593578b3..70a7c41f219 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 62c00660f6b..569e09e8ed7 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index b78af0a9616..01886b7db75 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.10-SNAPSHOT + 4.2.10 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index c496e41b99e..b7d783b3d97 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 995893bfa2e..7af0480799d 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.10-SNAPSHOT + 4.2.10 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index acd7ca46ef8..d2950ed518b 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 091710fafef..e1092a189f0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index ad34a7438a6..bf2bdc4979a 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10-SNAPSHOT + 4.2.10 operator-swagger From 54c796afe07c70513ae3d58714c5f3e22e364280 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 1 Nov 2024 14:27:55 -0400 Subject: [PATCH 215/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 96d60160ea3..1b35d66f8d4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 70a7c41f219..1313cd9a315 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 569e09e8ed7..f5d499fa7eb 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 01886b7db75..c5f3af80459 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.10 + 4.2.11-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index b7d783b3d97..035cc2b0dfd 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 7af0480799d..75f763d2c50 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.10 + 4.2.11-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index d2950ed518b..701b9376d9a 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index e1092a189f0..5f1ed255678 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index bf2bdc4979a..6085572d9c2 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.10 + 4.2.11-SNAPSHOT operator-swagger From 80d2557cf1afd13d01024c177547e1e738b6a059 Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Mon, 4 Nov 2024 15:56:36 +0000 Subject: [PATCH 216/356] [intermittent][OKE]ItLBTwoDomainsNginx, ItLBTwoDomainsTraefic, ItConfigDistributionStrategy tests failing while trying to access WLS server or application --- .../kubernetes/utils/ApplicationUtils.java | 29 +++++++++++++++++++ .../kubernetes/utils/CommonLBTestUtils.java | 6 ++++ .../kubernetes/utils/LoadBalancerUtils.java | 7 ++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java index fe61f98bf69..b10a6bfb82a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java @@ -8,6 +8,7 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.List; +import java.util.concurrent.Callable; import oracle.weblogic.kubernetes.logging.LoggingFacade; import org.awaitility.core.ConditionFactory; @@ -350,6 +351,34 @@ public static boolean callWebAppAndCheckForServerNameInResponse( return false; } + /** + * Call a web app and wait for the response code 200. + * @param curlCmd curl command to call the web app + * @return true if 200 response code is returned, false otherwise + */ + public static Callable callWebAppAndWaitTillReady(String curlCmd) { + LoggingFacade logger = getLogger(); + String httpStatusCode = "200"; + + return () -> { + final ExecResult result = ExecCommand.exec(curlCmd); + final String responseCode = result.stdout().trim(); + + if (result != null) { + logger.info("result.stdout: \n{0}", result.stdout()); + logger.info("result.stderr: \n{0}", result.stderr()); + logger.info("result.exitValue: \n{0}", result.exitValue()); + } + + if (result.exitValue() != 0 || !responseCode.equals(httpStatusCode)) { + logger.info("callWebApp did not return {0} response code, got {1}", httpStatusCode, responseCode); + return false; + } + + return true; + }; + } + /** * Call a web app and wait for the response code 200. * @param curlCmd curl command to call the web app diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java index 741ecbc05dd..a4f31a0b9af 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java @@ -913,6 +913,12 @@ public static void verifyAdminServerAccess(boolean isTLS, consoleAccessible = true; break; } + + try { + Thread.sleep(5000); + } catch (InterruptedException ignore) { + // ignore + } } catch (IOException | InterruptedException ex) { getLogger().severe(ex.getMessage()); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index 7d5a5037ffe..a186be4793e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -71,7 +71,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; -//import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyCommandResultContainsMsg; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; @@ -743,6 +743,11 @@ public static void createNginxIngressPathRoutingRules(String domainNamespace, + "/weblogic/ready --write-out %{http_code} -o /dev/null"; logger.info("Executing curl command {0}", curlCmd); + testUntil( + withLongRetryPolicy, + callWebAppAndWaitTillReady(curlCmd), + logger, + "Waiting until Web App available"); assertTrue(callWebAppAndWaitTillReady(curlCmd, 60)); } From 48b177527db12fdd6077ec5dc4eda82abeab029c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 4 Nov 2024 23:38:21 +0000 Subject: [PATCH 217/356] Merge branch 'notice-34-end-support' into 'main' Add notice for end of 3.4 support See merge request weblogic-cloud/weblogic-kubernetes-operator!4861 (cherry picked from commit ceef08c34d711ce8aeed1c24a45ae57c661abb49) 15020b6d Add notice for end of 3.4 support --- documentation/site/content/_index.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/documentation/site/content/_index.md b/documentation/site/content/_index.md index 7dc353cb79f..3d9f0456ee0 100644 --- a/documentation/site/content/_index.md +++ b/documentation/site/content/_index.md @@ -43,7 +43,11 @@ See the [Release Notes](https://github.com/oracle/weblogic-kubernetes-operator/r #### Operator earlier versions -Documentation for supported prior releases of the operator: [3.4](https://oracle.github.io/weblogic-kubernetes-operator/3.4/), [4.0](https://oracle.github.io/weblogic-kubernetes-operator/4.0/), and [4.1](https://oracle.github.io/weblogic-kubernetes-operator/4.1/). +{{% notice notice %}} +As of November 2024, version 3.4.x of the WebLogic Kubernetes Operator is no longer supported. +{{% /notice %}} + +Documentation for prior releases of the operator: [3.4](https://oracle.github.io/weblogic-kubernetes-operator/3.4/), [4.0](https://oracle.github.io/weblogic-kubernetes-operator/4.0/), and [4.1](https://oracle.github.io/weblogic-kubernetes-operator/4.1/). #### Backward compatibility guidelines From ce50d04b6d0b469c16e90d7c72d49848a0a27265 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 4 Nov 2024 20:38:41 -0500 Subject: [PATCH 218/356] Correct typo --- documentation/site/content/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/_index.md b/documentation/site/content/_index.md index 3d9f0456ee0..b7870e70192 100644 --- a/documentation/site/content/_index.md +++ b/documentation/site/content/_index.md @@ -43,7 +43,7 @@ See the [Release Notes](https://github.com/oracle/weblogic-kubernetes-operator/r #### Operator earlier versions -{{% notice notice %}} +{{% notice note %}} As of November 2024, version 3.4.x of the WebLogic Kubernetes Operator is no longer supported. {{% /notice %}} From f7222de90679e665ad105c882043bfc36b37561e Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Fri, 8 Nov 2024 03:40:27 +0800 Subject: [PATCH 219/356] On branch edburns-msft-6360-validate-script-locations (#5207) modified: documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md - `rm -rf`. Signed-off-by: Ed Burns --- .../content/samples/azure-kubernetes-service/domain-on-pv.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md index e44d9cabe0c..51c3fa50634 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md +++ b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md @@ -92,7 +92,7 @@ This sample requires [Domain creation images]({{< relref "/managing-domains/doma - Copy the sample to a new directory; for example, use the directory `/tmp/dpv-sample`. In the directory name, `dpv` is short for "domain on pv". Domain on PV is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). ```shell - $ rm /tmp/dpv-sample -f -r + $ rm -rf /tmp/dpv-sample $ mkdir /tmp/dpv-sample ``` From 09d3b35575382c7b7aee2428bf5af7d603876c8e Mon Sep 17 00:00:00 2001 From: xian_cao Date: Mon, 11 Nov 2024 18:40:17 +0000 Subject: [PATCH 220/356] Setup OCNE Jenkin Job for WKO integration tests on release/4.2 with Terraform script --- Jenkinsfile.ocne | 1 - Jenkinsfile.ocne19 | 667 ++++++++++++++++++ .../extensions/InitializationTasks.java | 7 +- .../weblogic/kubernetes/utils/IstioUtils.java | 2 + .../src/test/resources/ocne/ocne.create.sh | 241 +++++++ .../src/test/resources/ocne/ocne.delete.sh | 31 + .../resources/ocne/terraform/1.9/locals.tf | 16 + .../test/resources/ocne/terraform/1.9/main.tf | 173 +++++ .../oci-ocne-autoscaling/datasources.tf | 12 + .../modules/oci-ocne-autoscaling/locals.tf | 6 + .../1.9/modules/oci-ocne-autoscaling/main.tf | 101 +++ .../modules/oci-ocne-autoscaling/output.tf | 12 + .../modules/oci-ocne-autoscaling/variables.tf | 96 +++ .../modules/oci-ocne-autoscaling/versions.tf | 12 + .../1.9/modules/oci-ocne-compute/main.tf | 97 +++ .../1.9/modules/oci-ocne-compute/output.tf | 17 + .../1.9/modules/oci-ocne-compute/variables.tf | 98 +++ .../1.9/modules/oci-ocne-compute/versions.tf | 12 + .../modules/oci-ocne-images/datasources.tf | 20 + .../1.9/modules/oci-ocne-images/output.tf | 10 + .../1.9/modules/oci-ocne-images/variables.tf | 13 + .../1.9/modules/oci-ocne-images/versions.tf | 12 + .../1.9/modules/oci-ocne-kms/datasources.tf | 7 + .../1.9/modules/oci-ocne-kms/locals.tf | 9 + .../1.9/modules/oci-ocne-kms/main.tf | 27 + .../1.9/modules/oci-ocne-kms/output.tf | 17 + .../1.9/modules/oci-ocne-kms/variables.tf | 47 ++ .../1.9/modules/oci-ocne-kms/versions.tf | 12 + .../1.9/modules/oci-ocne-loadbalancer/main.tf | 55 ++ .../modules/oci-ocne-loadbalancer/output.tf | 23 + .../oci-ocne-loadbalancer/variables.tf | 69 ++ .../modules/oci-ocne-loadbalancer/versions.tf | 12 + .../1.9/modules/oci-ocne-network/main.tf | 127 ++++ .../1.9/modules/oci-ocne-network/output.tf | 19 + .../1.9/modules/oci-ocne-network/variables.tf | 70 ++ .../1.9/modules/oci-ocne-network/versions.tf | 11 + .../ocne-provision/config-file.yaml.example | 12 + .../1.9/modules/ocne-provision/datasources.tf | 9 + .../ocne-provision/files/config-edit.sh | 145 ++++ .../ocne-provision/files/config-read.sh | 139 ++++ .../files/provision.template.sh | 11 + .../1.9/modules/ocne-provision/main.tf | 114 +++ .../1.9/modules/ocne-provision/variables.tf | 187 +++++ .../1.9/modules/ocne-provision/versions.tf | 12 + .../cloudinit/autonomous.template.yaml | 17 + .../modules/terraform-oci-bastion/compute.tf | 54 ++ .../terraform-oci-bastion/datasources.tf | 93 +++ .../modules/terraform-oci-bastion/locals.tf | 15 + .../1.9/modules/terraform-oci-bastion/ons.tf | 46 ++ .../modules/terraform-oci-bastion/outputs.tf | 6 + .../scripts/notification.template.sh | 10 + .../modules/terraform-oci-bastion/security.tf | 25 + .../modules/terraform-oci-bastion/subnets.tf | 14 + .../terraform-oci-bastion/variables.tf | 127 ++++ .../modules/terraform-oci-bastion/versions.tf | 12 + .../files/agent_init.sh | 18 + .../files/apiserver_init.sh | 65 ++ .../locals.tf | 28 + .../terraform-oci-ocne-infrastructure/main.tf | 174 +++++ .../output.tf | 47 ++ .../variables.tf | 162 +++++ .../versions.tf | 12 + .../terraform-oci-vault/datasources.tf | 86 +++ .../1.9/modules/terraform-oci-vault/main.tf | 111 +++ .../1.9/modules/terraform-oci-vault/output.tf | 28 + .../templates/cloudinit.template | 128 ++++ .../terraform-oci-vault/templates/fetch_token | 34 + .../templates/ocne-cert-engine-policy.hcl | 20 + .../templates/vault.template | 23 + .../terraform-oci-vault/templates/vault_env | 8 + .../terraform-oci-vault/templates/vault_setup | 98 +++ .../modules/terraform-oci-vault/variables.tf | 181 +++++ .../modules/terraform-oci-vault/versions.tf | 12 + .../resources/ocne/terraform/1.9/output.tf | 270 +++++++ .../resources/ocne/terraform/1.9/provider.tf | 12 + .../terraform/1.9/terraform.tfvars.template | 41 ++ .../resources/ocne/terraform/1.9/variables.tf | 344 +++++++++ .../resources/ocne/terraform/1.9/versions.tf | 13 + validateScripts.sh | 5 +- 79 files changed, 5122 insertions(+), 7 deletions(-) create mode 100644 Jenkinsfile.ocne19 create mode 100755 integration-tests/src/test/resources/ocne/ocne.create.sh create mode 100644 integration-tests/src/test/resources/ocne/ocne.delete.sh create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/locals.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/datasources.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/locals.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/datasources.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/locals.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/config-file.yaml.example create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/datasources.tf create mode 100755 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-edit.sh create mode 100755 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-read.sh create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/provision.template.sh create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/cloudinit/autonomous.template.yaml create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/compute.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/datasources.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/locals.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/ons.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/outputs.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/scripts/notification.template.sh create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/security.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/subnets.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/agent_init.sh create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/apiserver_init.sh create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/locals.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/datasources.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/main.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/cloudinit.template create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/fetch_token create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/ocne-cert-engine-policy.hcl create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault.template create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_env create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_setup create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/versions.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/output.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/provider.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/terraform.tfvars.template create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/variables.tf create mode 100644 integration-tests/src/test/resources/ocne/terraform/1.9/versions.tf diff --git a/Jenkinsfile.ocne b/Jenkinsfile.ocne index 7d9d0f52da1..5c9e7fc6bcc 100644 --- a/Jenkinsfile.ocne +++ b/Jenkinsfile.ocne @@ -96,7 +96,6 @@ pipeline { description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', //defaultValue7.6: 'ocid1.image.oc1.phx.aaaaaaaancucg25pze6m52agxwxmmfszvsdnhp3kvim53pts2tw6mik4xtha' defaultValue: 'ocid1.image.oc1.phx.aaaaaaaaaizmtmozeudeeuq7o5ir7dkl2bkxbbb3tgomshqbqn6jpomrsjza' - ) string(name: 'KUBECTL_VERSION', description: 'kubectl version', diff --git a/Jenkinsfile.ocne19 b/Jenkinsfile.ocne19 new file mode 100644 index 00000000000..f69bc6da3a7 --- /dev/null +++ b/Jenkinsfile.ocne19 @@ -0,0 +1,667 @@ +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// + +CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=olcne-srg + H 5 * * * % MAVEN_PROFILE_NAME=olcne-mrg + H 14 * * * % MAVEN_PROFILE_NAME=olcne-sequential''' + +pipeline { + agent { label 'large' } + options { + timeout(time: 1800, unit: 'MINUTES') + //disableConcurrentBuilds() + } + triggers { + // timer trigger for "nightly build" + parameterizedCron(env.JOB_NAME == 'wko-ocne19-release42-nightly' ? + CRON_SETTINGS : '') + } + + tools { + maven 'maven-3.8.7' + jdk 'jdk21' + } + + environment { + ocir_host = "${env.WKT_OCIR_HOST}" + wko_tenancy = "${env.WKT_TENANCY}" + wko_images_rep = '${wko_tenancy}/wkt/infra' + wko_region = "${env.WKT_REGION}" + compartment_id = "${env.WKT_TEST_COMPARTMENT_ID}" + ocir_creds = 'wkt-ocir-creds' + wko_files_bucket_token = 'wko-system-test-files-bucket-par-token' + + oci_profile = 'DEFAULT' + jenkins_user = sh(returnStdout: true, script: "id -nu").trim() + jenkins_home_directory = sh(returnStdout: true, script: "getent passwd ${jenkins_user} | cut -d: -f6").trim() + OCI_CLI_CONFIG_FILE = "${jenkins_home_directory}/.oci/config" + + oci_config_file_creds = 'wkt-oci-config-file' + oci_signing_key_creds = 'wkt-oci-signing-key' + + compartmentName='test' + + wkobuild_cert_fingerprint = 'wkotestcert_fingerprint' + wkobuild_user_ocid = 'wkotest-user-ocid' + + wkotest_ssh_pubcert = "${WORKSPACE}/.ssh/wkotestkey.pub" + wkotest_ssh_pk = "${WORKSPACE}/.ssh/wkotestkey" + + OCNE='true' + + outdir = "${WORKSPACE}/staging" + result_root = "${outdir}/wl_k8s_test_results" + pv_root_parent = "${outdir}/k8s-pvroot" + pv_root = "${pv_root_parent}/pvroot${BUILD_ID}" + pv_root_for_sampletest = "/shared" + + start_time = sh(script: 'date +"%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" + + kubeconfig_file = "${WORKSPACE}/terraform/terraform/1.9/kubeconfig" + availability_domain = "${env.JOB_NAME == 'wko-ocne19-release42-nightly' ? 'mFEn:PHX-AD-1' : 'mFEn:PHX-AD-1'}" + PARALLEL_RUN = "${env.JOB_NAME == 'wko-ocne19-release42-nightly' ? true : false}" + } + + parameters { + + string(name: 'BRANCH', + description: 'The branch for weblogic-kubernetes-operator project', + defaultValue: "release/4.2" + ) + + choice(name: 'MAVEN_PROFILE_NAME', + description: 'Profile to use in mvn command to run the tests. Possible values are olcne (the default), wko-olcne-cert and integration-tests. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + choices: [ + 'olcne-srg', + 'olcne-mrg', + 'olcne-sequential', + 'integration-tests' + ] + ) + + string(name: 'IT_TEST', + description: 'Comma separated list of individual It test classes to be run e.g., ItParameterizedDomain, ItMiiUpdateDomainConfig, ItMiiDynamicUpdate*, ItMiiMultiMode', + defaultValue: '' + ) + + choice(name: 'KUBECTL_VERSION', + description: 'kubectl version', + choices: [ + '1.29.4', + '1.29.2', + '1.29.8', + '1.30.0', + '1.31.0' + ] + ) + string(name: 'HELM_VERSION', + description: 'Helm version', + defaultValue: '3.11.2' + ) + choice(name: 'ISTIO_VERSION', + description: 'Istio version', + choices: [ + '1.23.0', + '1.17.2', + '1.16.1', + '1.13.2', + '1.12.6', + '1.11.1', + '1.10.4', + '1.9.9' + ] + ) + string(name: 'NUMBER_OF_THREADS', + description: 'Number of threads to run the classes in parallel, default is 2.', + defaultValue: "2" + ) + string(name: 'WDT_DOWNLOAD_URL', + description: 'URL to download WDT.', + //defaultValue: 'https://github.com/oracle/weblogic-deploy-tooling/releases/latest' + ) + string(name: 'WIT_DOWNLOAD_URL', + description: 'URL to download WIT.', + //defaultValue: 'https://github.com/oracle/weblogic-image-tool/releases/latest' + ) + string(name: 'REPO_REGISTRY', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + choice(name: 'BASE_IMAGES_REPO', + choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], + description: 'Repository to pull the base images. Make sure to modify the image names if you are modifying this parameter value.' + ) + string(name: 'TEST_IMAGES_REPO', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + string(name: 'WEBLOGIC_IMAGE_NAME', + description: 'WebLogic base image name. Default is the image name in OCIR. Use middleware/weblogic for OCR.', + defaultValue: 'test-images/weblogic' + ) + string(name: 'WEBLOGIC_IMAGE_TAG', + description: '12.2.1.3 (12.2.1.3-ol7) , 12.2.1.3-dev (12.2.1.3-dev-ol7), 12.2.1.3-ol8, 12.2.1.3-dev-ol8, 12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '12.2.1.4' + ) + string(name: 'FMWINFRA_IMAGE_NAME', + description: 'FWM Infra image name. Default is the image name in OCIR. Use middleware/fmw-infrastructure for OCR.', + defaultValue: 'test-images/fmw-infrastructure' + ) + string(name: 'FMWINFRA_IMAGE_TAG', + description: 'FWM Infra image tag', + defaultValue: '12.2.1.4' + ) + string(name: 'DB_IMAGE_NAME', + description: 'Oracle DB image name. Default is the image name in OCIR, use database/enterprise for OCR.', + defaultValue: 'test-images/database/enterprise' + ) + string(name: 'DB_IMAGE_TAG', + description: 'Oracle DB image tag', + defaultValue: '12.2.0.1-slim' + ) + string(name: 'MONITORING_EXPORTER_BRANCH', + description: '', + defaultValue: 'main' + ) + string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', + description: '', + defaultValue: '2.1.3' + ) + string(name: 'PROMETHEUS_CHART_VERSION', + description: '', + defaultValue: '17.0.0' + ) + string(name: 'GRAFANA_CHART_VERSION', + description: '', + defaultValue: '6.44.11' + ) + booleanParam(name: 'COLLECT_LOGS_ON_SUCCESS', + description: 'Collect logs for successful runs. Default is false.', + defaultValue: false + ) + string(name: 'REMOTECONSOLE_VERSION', + description: 'RemoteConsole version.', + defaultValue: '2.4.7' + ) + } + + stages { + stage('Filter unwanted branches') { + stages { + stage('Workaround JENKINS-41929 Parameters bug') { + steps { + echo 'Initialize parameters as environment variables due to https://issues.jenkins-ci.org/browse/JENKINS-41929' + evaluate """${def script = ""; params.each { k, v -> script += "env.${k} = '''${v}'''\n" }; return script}""" + } + } + stage ('Echo environment') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + env|sort + java -version + mvn --version + python --version + docker version + ulimit -a + ulimit -aH + ''' + } + } + stage('Make Workspace bin directory') { + steps { + sh "mkdir -m777 -p ${WORKSPACE}/bin" + } + } + stage('Install Helm') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=helm/helm-v${HELM_VERSION}.tar.gz --file=helm.tar.gz \ + --auth=instance_principal + tar zxf helm.tar.gz + mv linux-amd64/helm ${WORKSPACE}/bin/helm + rm -rf linux-amd64 + helm version + ''' + } + } + + stage ('Install kubectl') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=kubectl/kubectl-v${KUBECTL_VERSION} --file=${WORKSPACE}/bin/kubectl \ + --auth=instance_principal + chmod +x ${WORKSPACE}/bin/kubectl + kubectl version --client=true + ''' + } + } + + stage('Delete known_hosts') { + steps { + sh "rm -f ~/.ssh/known_hosts" + } + } + + stage('Setup OCI config file ') { + + steps { + withCredentials([file(credentialsId: "${oci_config_file_creds}", variable: 'OCI_CONFIG_FILE'), + file(credentialsId: "${oci_signing_key_creds}", variable: 'OCI_SIGNING_KEY')]) { + sh "mkdir -p ${jenkins_home_directory}/.oci" + sh "sed -e \"s:__jenkins_home__:${jenkins_home_directory}:g\" \"\$OCI_CONFIG_FILE\" > ${jenkins_home_directory}/.oci/config" + sh "cp \"\$OCI_SIGNING_KEY\" ${jenkins_home_directory}/.oci/oci-signing-key.pem" + } + sh ''' + cat ${jenkins_home_directory}/.oci/config + chmod 600 ${jenkins_home_directory}/.oci/config + ''' + } + } + + // Creating terraform property file. + stage('Setup terraform property file ') { + environment { + OCI_PROP_FILE="${WORKSPACE}/terraform/oci.prop" + FINGERPRINT = 'wkobuild_cert_fingerprint' + USER_OCID = 'wkobuild_user_ocid' + WKOBUILD_PUBLIC_CERT = "${wkotest_ssh_pubcert}" + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + + sh ''' + TENANCY_OCID=$(grep -Po '(?<=tenancy=).*' ${jenkins_home_directory}/.oci/config) + USER_OCID=$(grep -Po '(?<=user=).*' ${jenkins_home_directory}/.oci/config) + FINGERPRINT=$(grep -Po '(?<=fingerprint=).*' ${jenkins_home_directory}/.oci/config) + + network_compartment_ocid=$(oci iam compartment list --compartment-id-in-subtree true --all | jq --arg compname Networks '.data[] | select(."name"=="Networks")' | jq -r ."id") + vcn_ocid=$(oci network vcn list --compartment-id=${network_compartment_ocid} | jq -r '.data[] | select(."display-name" == "CorpDev2-phx.vcn")' | jq -r ."id") + private_subnet_ocid=$(oci network subnet list --compartment-id=${network_compartment_ocid} | jq '.data[] | select(."display-name" == "VCN2-Subnet1-Regional")' | jq -r ."id") + export WKT_TENANCY_OCID=${TENANCY_OCID} + export WKT_USER_OCID=${USER_OCID} + mkdir -p ${WORKSPACE}/terraform + mkdir -p ${WORKSPACE}/oci + cat ${jenkins_home_directory}/.oci/config + mkdir -p ${WORKSPACE}/.ssh + cd ${WORKSPACE}/.ssh + ssh-keygen -b 2048 -t rsa -q -f wkotestkey -N "" + chmod 600 ${wkotest_ssh_pk} + + COMPARTMENT_OCID=${compartment_id} + + export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True + + export OCI_PROP_FILE=${OCI_PROP_FILE} + + ssh_pubkey=`cat ${wkotest_ssh_pubcert}` + ssh_pk=`cat ${wkotest_ssh_pk}` + + ################# + echo "Generating property file oci.prop for terraform scripts" + +cat > $OCI_PROP_FILE <> ${WORKSPACE}/.mvn/maven.config + fi + if [ "${MAVEN_PROFILE_NAME}" == "olcne-sequential" ]; then + PARALLEL_RUN='false' + fi + + echo "MAVEN_PROFILE_NAME:" $MAVEN_PROFILE_NAME + echo "PARALLEL_RUN:" $PARALLEL_RUN + echo "-Dwko.it.wle.download.url=\"${wle_download_url}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.result.root=\"${result_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.pv.root=\"${pv_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.k8s.nodeport.host=\"${K8S_NODEPORT_HOST}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.nfs.server=\"${NFS_SERVER}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.istio.version=\"${ISTIO_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DPARALLEL_CLASSES=\"${PARALLEL_RUN}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DNUMBER_OF_THREADS=\"${NUMBER_OF_THREADS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wdt.download.url=\"${WDT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wit.download.url=\"${WIT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.repo=\"${BASE_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.repo=\"${TEST_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.name=\"${WEBLOGIC_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.tag=\"${WEBLOGIC_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.name=\"${FMWINFRA_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.tag=\"${FMWINFRA_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.name=\"${DB_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.tag=\"${DB_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.branch=\"${MONITORING_EXPORTER_BRANCH}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.webapp.version=\"${MONITORING_EXPORTER_WEBAPP_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.grafana.chart.version=\"${GRAFANA_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.collect.logs.on.success=\"${COLLECT_LOGS_ON_SUCCESS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + + echo "${WORKSPACE}/.mvn/maven.config contents:" + cat "${WORKSPACE}/.mvn/maven.config" + cp "${WORKSPACE}/.mvn/maven.config" "${result_root}" + ''' + } + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + withCredentials([ + usernamePassword(credentialsId: "${ocir_creds}", usernameVariable: 'OCIR_USER', passwordVariable: 'OCIR_PASS') + ]) { + sh ''' + + prefix=ocne-tf-jenkin-${BUILD_ID} + k8s_master_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-control-plane-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` + k8s_master_instance_private_ip=`oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_master_instance_id} |jq -r '.data[] | select(."hostname-label" == null) | ."private-ip"'` + + export HTTPS_PROXY='http://www-proxy-hqdc.us.oracle.com:80' + export https_proxy='http://www-proxy-hqdc.us.oracle.com:80' + export HTTP_PROXY='http://www-proxy-hqdc.us.oracle.com:80' + export http_proxy='http://www-proxy-hqdc.us.oracle.com:80' + + export NO_PROXY="localhost,127.0.0.1,.us.oracle.com,.oraclecorp.com,/var/run/docker.sock,${k8s_master_instance_private_ip}" + export no_proxy="localhost,127.0.0.1,.us.oracle.com,.oraclecorp.com,/var/run/docker.sock,${k8s_master_instance_private_ip}" + export PATH=${runtime_path} + export OKE_CLUSTER="false" + export OCNE="true" + export OKD="false" + export KUBECONFIG=${kubeconfig_file} + export BASE_IMAGES_REPO_USERNAME="${OCIR_USER}" + export BASE_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export BASE_IMAGES_REPO_EMAIL="noreply@oracle.com" + export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" + export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" + if [[ -n "${IT_TEST}" && "${IT_TEST}" != "**/It*" ]]; then + echo 'Overriding MAVEN_PROFILE_NAME to integration-test when running individual test(s)' + export MAVEN_PROFILE_NAME="integration-tests" + fi + echo "MAVEN_PROFILE_NAME:" $MAVEN_PROFILE_NAME + if ! mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/ocnetest.log"; then + echo "integration-tests failed" + exit 1 + fi + ''' + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + export KUBECONFIG=${kubeconfig_file} + export OCI_CLI_PROFILE=${oci_profile} + mkdir -m777 -p ${result_root}/kubelogs + mkdir -m777 -p "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + sudo mv -f ${result_root}/* "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + + ''' + + archiveArtifacts(artifacts: + "logdir/${BUILD_TAG}/wl_k8s_test_results/diagnostics/**/*,logdir/${BUILD_TAG}/wl_k8s_test_results/workdir/liftandshiftworkdir/**/*") + junit(testResults: 'integration-tests/target/failsafe-reports/*.xml', allowEmptyResults: true) + } + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + export KUBECONFIG=${kubeconfig_file} + export OCI_PROP_FILE="${WORKSPACE}/terraform/oci.prop" + + if [ -f "$OCI_PROP_FILE" ] && [ -f "${WORKSPACE}/terraform/ocne.delete.sh" ]; then + ${WORKSPACE}/terraform/ocne.delete.sh ${OCI_PROP_FILE} ${WORKSPACE}/terraform/terraform/1.9 + fi + + rm -f ${jenkins_home_directory}/.oci/config + rm -f ${jenkins_home_directory}/.oci/oci-signing-key.pem + rm -f ${WORKSPACE}/.ssh/* + rm -rf ${WORKSPACE}/.mvn + sudo rm -rf ${pv_root} + sudo umount ${pv_root_parent} + sudo rm -rf ${pv_root_for_sampletest}/domains + sudo rm -rf ${pv_root_for_sampletest}/logs + sudo umount ${pv_root_for_sampletest} + ''' + } + } + } + + stage ('Sync') { + when { + anyOf { + branch 'main' + branch 'release/4.2' + } + anyOf { + not { triggeredBy 'TimerTrigger' } + tag 'v*' + } + } + steps { + build job: "wkt-sync", parameters: [ string(name: 'REPOSITORY', value: 'weblogic-kubernetes-operator') ] + } + } + } +} + + diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 1346ec76da0..e08757ad7a7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -325,8 +325,7 @@ public void beforeAll(ExtensionContext context) { // set initialization success to true, not counting the istio installation as not all tests use istio isInitializationSuccessful = true; - if ((!OKD && !OCNE && !CRIO) - || (OCNE && !CRIO && !assertDoesNotThrow(() -> Namespace.exists("istio-system")))) { + if (!OKD && !CRIO) { logger.info("Installing istio before any test suites are run"); installIstio(); } @@ -379,11 +378,11 @@ public void close() { if (SKIP_CLEANUP) { logger.info("Skipping RESULTS_ROOT clean up after test execution"); } else { - if (!OKD && !OCNE && !CRIO) { + if (!OKD && !CRIO) { logger.info("Uninstall istio after all test suites are run"); uninstallIstio(); } - if (!OKD && !OKE_CLUSTER && !OCNE && !CRIO) { + if (!OKD && !OKE_CLUSTER && !CRIO) { logger.info("Delete istio-system namespace after all test suites are run"); deleteNamespace("istio-system"); deleteNamespace(ORACLE_OPERATOR_NS); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index e47f2c1bfbf..a360c3e9dae 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -104,6 +104,8 @@ public static void installIstio() { logger.info("replace istio installation hub in File {0}", installScript); assertDoesNotThrow(() -> replaceStringInFile(installScript, "gcr.io", ocneIstioRepo), String.format("Failed to replace string in File %s", installScript)); + assertDoesNotThrow(() -> replaceStringInFile(installScript, "--auth=instance_principal", " "), + String.format("Failed to replace string in File %s", installScript)); } String arch = "linux-amd64"; if (ARM) { diff --git a/integration-tests/src/test/resources/ocne/ocne.create.sh b/integration-tests/src/test/resources/ocne/ocne.create.sh new file mode 100755 index 00000000000..9dbc2be307a --- /dev/null +++ b/integration-tests/src/test/resources/ocne/ocne.create.sh @@ -0,0 +1,241 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +prop() { + grep "${1}" ${propsFile}| grep -v "#" | cut -d'=' -f2 +} + +generateTFVarFile() { + tfVarsFile=${terraformVarDir}/terraform.tfvars + rm -f ${tfVarsFile} + cp ${terraformVarDir}/terraform.tfvars.template $tfVarsFile + chmod 777 $tfVarsFile + + sed -i -e "s:@OCI_TENANCY_ID@:${tenancy_id}:g" ${tfVarsFile} + sed -i -e "s:@OCI_COMPARTMENT_ID@:${compartment_id}:g" ${tfVarsFile} + sed -i -e "s:@OCI_USER_ID@:${user_id}:g" ${tfVarsFile} + sed -i -e "s/@OCI_FINGERPRINT@/"${fingerprint}"/g" ${tfVarsFile} + sed -i -e "s:@OCI_API_PRIVATE_KEY_PATH@:${api_private_key_path}:g" ${tfVarsFile} + + sed -i -e "s:@OCI_REGION@:${region}:g" ${tfVarsFile} + sed -i -e "s/@OCI_AVAILABILITY_DOMAIN_ID@/"${availability_domain_id}"/g" ${tfVarsFile} + sed -i -e "s:@OCI_INSTANCE_PREFIX@:${prefix}:g" ${tfVarsFile} + + sed -i -e "s:@OCI_DEPLOY_NETWORKING@:${deploy_networking}:g" ${tfVarsFile} + sed -i -e "s:@OCI_SUBNET_ID@:${subnet_id}:g" ${tfVarsFile} + sed -i -e "s:@OCI_VCN_ID@:${vcn_id}:g" ${tfVarsFile} + + sed -i -e "s:@OCI_SSH_PUBLIC_KEY_PATH@:${ssh_public_key_path}:g" ${tfVarsFile} + sed -i -e "s:@OCI_SSH_PRIVATE_KEY_PATH@:${ssh_private_key_path}:g" ${tfVarsFile} + + sed -i -e "s:@OCI_ENABLE_BASTION@:${enable_bastion}:g" ${tfVarsFile} + + sed -i -e "s:@OCI_VIRTUAL_IP@:${virtual_ip}:g" ${tfVarsFile} + + sed -i -e "s:@OCNE_CONTROL_PLANE_NODE_COUNT@:${control_plane_node_count}:g" ${tfVarsFile} + sed -i -e "s:@OCNE_WORKER_NODE_COUNT@:${worker_node_count}:g" ${tfVarsFile} + sed -i -e "s:@OCNE_ENVIRONMENT_NAME@:${environment_name}:g" ${tfVarsFile} + sed -i -e "s:@OCNE_K8S_CLUSTER_NAME@:${kubernetes_name}:g" ${tfVarsFile} + + sed -i -e "s:@OCNE_VERSION@:${ocne_version}:g" ${tfVarsFile} + + sed -i -e "s#@HTTP_PROXY@#${http_proxy}#g" ${tfVarsFile} + sed -i -e "s:@NO_PROXY@:${no_proxy}:g" ${tfVarsFile} + + echo "Generated TFVars file [${tfVarsFile}]" + cat "${tfVarsFile}" +} + +setupTerraform() { + mkdir ${terraformDir} + cd ${terraformDir} + if [[ "${OSTYPE}" == "darwin"* ]]; then + os_type="darwin" + elif [[ "${OSTYPE}" == "linux"* ]]; then + os_type="linux" + else + echo "Unsupported OS" + fi + curl -O https://releases.hashicorp.com/terraform/1.8.1/terraform_1.8.1_${os_type}_${platform}64.zip + unzip terraform_1.8.1_${os_type}_${platform}64.zip + chmod +x ${terraformDir}/terraform + + # install yq + wget https://github.com/mikefarah/yq/releases/download/v4.35.2/yq_${os_type}_${platform}64.tar.gz + tar -zxvf yq_${os_type}_${platform}64.tar.gz + mv yq_${os_type}_${platform}64 yq + + export PATH=${terraformDir}:${PATH} +} + +deleteOlderVersionTerraformOCIProvider() { + if [ -d ~/.terraform.d/plugins ]; then + echo "Deleting older version of terraform plugins dir" + rm -rf ~/.terraform.d/plugins + fi + if [ -d ${terraformVarDir}/.terraform ]; then + rm -rf ${terraformVarDir}/.terraform + fi + if [ -e ~/.terraformrc ]; then + rm ~/.terraformrc + fi +} + +createCluster () { + cd ${terraformVarDir} + echo "terraform init -var-file=${terraformVarDir}/terraform.tfvars" + terraform init -var-file=${terraformVarDir}/terraform.tfvars + echo "terraform plan -var-file=${terraformVarDir}/terraform.tfvars" + terraform plan -var-file=${terraformVarDir}/terraform.tfvars + echo "terraform apply -auto-approve -var-file=${terraformVarDir}/terraform.tfvars" + terraform apply -auto-approve -var-file=${terraformVarDir}/terraform.tfvars +} + +checkKubernetesCliConnection() { + echo "Confirming ${KUBERNETES_CLI:-kubectl} can connect to the server..." + + # Get the OCNE cluster control node private IP + echo "command to get k8s_master_instance_id: oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-control-plane-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'" + k8s_master_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-control-plane-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` + echo "command to get k8s_master_instance_private_ip: oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_master_instance_id} |jq -r '.data[]."private-ip"'" + k8s_master_instance_private_ips=`oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_master_instance_id} |jq -r '.data[]."private-ip"'` + + if [ -z "$k8s_master_instance_private_ips" ]; then + echo "[ERROR] No active cluster found with name ${kubernetes_name}." + exit 1 + fi + + echo "OCNE K8S cluster control node private ip: ### $k8s_master_instance_private_ips ###" + declare -a k8s_master_instance_private_ip=(${k8s_master_instance_private_ips//\n/ }) + + local local_no_proxy=${no_proxy} + for i in "${k8s_master_instance_private_ip[@]}"; do + local_no_proxy+=",$i" + done + export NO_PROXY="$local_no_proxy" + echo "NO_PROXY=$NO_PROXY" + + export KUBECONFIG=${terraformVarDir}/kubeconfig + echo "KUBECONFIG=$KUBECONFIG" + + local myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) + + if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then + echo "[ERROR] Unable to connect to the server: net/http: TLS handshake timeout" + echo '- could not talk to OCNE cluster, aborting' + + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/terraform.tfvars" + exit 1 + fi + if echo "$myline_output" | grep -q "couldn't get current server API group"; then + echo "[ERROR] Unable to connect to the server: couldn't get current server API group, connection refused" + echo '- check errors during OKE cluster creation' + echo '- could not talk to OCNE cluster, aborting' + + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/terraform.tfvars" + exit 1 + fi +} + +checkClusterRunning() { + checkKubernetesCliConnection + + local prefix=${prefix} + declare -a myline=($(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${prefix}" | awk '{print $2}')) + + local max=100 + local count=1 + + for (( i = 0; i < ${#myline[@]} ; i++ )); do + while [ "${myline[i]}" != "Ready" ] && [ $count -le $max ]; do + echo "[ERROR] Some Nodes in the Cluster are not in the Ready Status, sleeping for 10s..." + sleep 10 + myline=($(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${prefix}" | awk '{print $2}')) + echo "myline[i]: ${myline[i]}" + echo "Status is ${myline[i]} Iter [$count/$max]" + count=$((count + 1)) + done + done + + local NODES=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide | grep "${prefix}" | wc -l) + local number_nodes=$(($control_plane_node_count + $worker_node_count)) + if [ "$NODES" -eq "$number_nodes" ]; then + echo '- looks good' + else + echo '- could not talk to OCNE cluster, aborting' + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + exit 1 + fi + + if [ $count -gt $max ]; then + echo "[ERROR] Unable to start the nodes in the OCNE cluster after 200s" + cd "${terraformVarDir}" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + exit 1 + fi +} + + +#MAIN +propsFile=${1:-$PWD/oci.props} +terraformVarDir=${2:-$PWD} +platform=${3:-amd} + +#grep props's values from oci.props file + +tenancy_id=$(prop 'tenancy_id') +compartment_id=$(prop 'compartment_id') +user_id=$(prop 'user_id') +fingerprint=$(prop 'fingerprint') +api_private_key_path=$(prop 'api_private_key_path') + +region=$(prop 'region') +availability_domain_id=$(prop 'availability_domain_id') +prefix=$(prop 'prefix') + +deploy_networking=$(prop 'deploy_networking') +subnet_id=$(prop 'subnet_id') +vcn_id=$(prop 'vcn_id') + +ssh_public_key_path=$(prop 'ssh_public_key_path') +ssh_private_key_path=$(prop 'ssh_private_key_path') + +enable_bastion=$(prop 'enable_bastion') + +virtual_ip=$(prop 'virtual_ip') + +control_plane_node_count=$(prop 'control_plane_node_count') +worker_node_count=$(prop 'worker_node_count') +environment_name=$(prop 'environment_name') +kubernetes_name=$(prop 'kubernetes_name') + +ocne_version=$(prop 'ocne_version') + +http_proxy=$(prop 'http_proxy') +no_proxy=$(prop 'no_proxy') + +terraformDir=$(prop 'terraform.installdir') + +# generate terraform configuration file with name $(clusterTFVarsFile).tfvar +generateTFVarFile + +# cleanup previously installed terraform binaries +rm -rf ${terraformDir} + +# download terraform binaries into ${terraformDir} +setupTerraform + +# clean previous versions of terraform oci provider +deleteOlderVersionTerraformOCIProvider + +# run terraform init,plan,apply to create OCNE cluster based on the provided tfvar file ${tfVarsFile} +createCluster + +#check status of OCNE cluster nodes, destroy if can not access them +export KUBECONFIG=${terraformVarDir}/kubeconfig +checkClusterRunning +echo "${kubernetes_name} is up and running" diff --git a/integration-tests/src/test/resources/ocne/ocne.delete.sh b/integration-tests/src/test/resources/ocne/ocne.delete.sh new file mode 100644 index 00000000000..50c356718f8 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/ocne.delete.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# +# This script deletes provisioned OKE Kubernetes cluster using terraform (https://www.terraform.io/) +# + +set -o errexit +set -o pipefail + +prop() { + grep "${1}" ${oci_property_file}| grep -v "#" | cut -d'=' -f2 +} + +deleteOCNECluster() { + cd ${terraform_script_dir} + terraform init -var-file=${terraform_script_dir}/terraform.tfvars + terraform plan -var-file=${terraform_script_dir}/terraform.tfvars + terraform destroy -auto-approve -var-file=${terraform_script_dir}/terraform.tfvars +} + + +#MAIN +oci_property_file=${1:-$PWD/oci.props} +terraform_script_dir=${2:-$PWD} + +terraformDir=$(prop 'terraform.installdir') +export PATH=${terraformDir}:$PATH + +echo 'Deleting cluster' +deleteOCNECluster || true diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/locals.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/locals.tf new file mode 100644 index 00000000000..2eda8ca1242 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/locals.tf @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +locals { + provision_modes_map = { + provision_mode_ocne = "OCNE" + provision_mode_infrastucture = "Infrastructure" + } + + provision_modes_values_list = values(local.provision_modes_map) +} + +locals { + secret_name = "${var.prefix}-${var.secret_name}" + ocne_secret_name = "${var.prefix}-${var.ocne_secret_name}" +} \ No newline at end of file diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/main.tf new file mode 100644 index 00000000000..5ce5e33ac79 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/main.tf @@ -0,0 +1,173 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +resource "null_resource" "variable_validation" { + lifecycle { + postcondition { + condition = (contains(local.provision_modes_values_list, var.provision_mode)) + error_message = "Invalid provision mode: ${var.provision_mode}. Valid provision modes are: ${join(", ", local.provision_modes_values_list)}" + } + postcondition { + condition = (!var.deploy_networking || var.enable_bastion) + error_message = "enable_bastion must be true if deploy_networking is true" + } + postcondition { + condition = (!var.enable_bastion || (var.enable_bastion && var.bastion_private_key_path != "")) + error_message = "bastion_private_key_path must be set if enable_bastion is true" + } + } +} + +data "external" "ocne_config" { + program = ["sh", "${path.module}/modules/ocne-provision/files/config-read.sh"] + query = { + path = var.config_file_path + ov = var.ocne_version + en = var.environment_name + kn = var.kubernetes_name + } +} + +resource "null_resource" "ocne_config_validation" { + depends_on = [data.external.ocne_config] + lifecycle { + postcondition { + condition = (length(data.external.ocne_config.result.error) == 0) + error_message = data.external.ocne_config.result.error + } + } +} + +module "oci-ocne-network" { + source = "./modules/oci-ocne-network" + count = (var.deploy_networking && var.enable_bastion) || var.enable_bastion ? 1 : 0 + + compartment_id = var.compartment_id + ssh_public_key_path = var.ssh_public_key_path + prefix = var.prefix + deploy_networking = var.deploy_networking + enable_bastion = var.enable_bastion + vcn_id = var.vcn_id + ig_route_id = var.ig_route_id + nat_route_id = var.nat_route_id + freeform_tags = var.freeform_tags +} + +module "bastion" { + source = "./modules/terraform-oci-bastion" + count = var.enable_bastion ? 1 : 0 + + bastion_shape = var.bastion_shape + tenancy_id = var.tenancy_id + compartment_id = var.compartment_id + ig_route_id = var.deploy_networking ? module.oci-ocne-network[0].ig_route_id : var.ig_route_id + vcn_id = var.deploy_networking ? module.oci-ocne-network[0].vcn_id : var.vcn_id + prefix = var.prefix + ssh_public_key_path = var.ssh_public_key_path + enable_notification = var.enable_notification + freeform_tags = var.freeform_tags +} + +module "infrastructure" { + depends_on = [null_resource.variable_validation] + source = "./modules/terraform-oci-ocne-infrastructure" + + availability_domain_id = var.availability_domain_id + compartment_id = var.compartment_id + prefix = var.prefix + subnet_id = var.deploy_networking ? module.oci-ocne-network[0].private_subnet_id.*.id[0] : var.subnet_id + instance_shape = var.instance_shape + image_ocid = var.image_ocid + os_version = var.os_version + kernel_version = var.kernel_version + load_balancer_shape = var.load_balancer_shape + ssh_public_key_path = var.ssh_public_key_path + ssh_private_key_path = var.ssh_private_key_path + control_plane_node_count = var.control_plane_node_count + worker_node_count = var.worker_node_count + standalone_api_server = var.standalone_api_server + yum_repo_url = var.yum_repo_url + ocne_version = data.external.ocne_config.result.version + enable_bastion = var.enable_bastion + bastion_public_ip = var.enable_bastion ? module.bastion[0].bastion_public_ip : var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key_path = var.enable_bastion || var.bastion_public_ip != "" ? var.bastion_private_key_path : "" + compute_user = var.compute_user + freeform_tags = var.freeform_tags + virtual_ip = var.virtual_ip +} + +module "vault" { + source = "./modules/terraform-oci-vault" + count = var.use_vault ? 1 : 0 + + user_id = var.user_id + region = var.region + tenancy_id = var.tenancy_id + fingerprint = var.fingerprint + api_private_key_path = var.api_private_key_path + private_key_password = var.private_key_password + availability_domain_id = var.availability_domain_id + compartment_id = var.compartment_id + prefix = "${var.prefix}-vault" + subnet_id = var.deploy_networking ? module.oci-ocne-network[0].private_subnet_id.*.id[0] : var.subnet_id + instance_shape = var.instance_shape + ssh_public_key_path = var.ssh_public_key_path + ssh_private_key_path = var.ssh_private_key_path + load_balancer_port = "8200" + proxy = var.proxy + vault_ocid = var.vault_ocid + key_ocid = var.key_ocid + vault_namespace = var.vault_namespace + load_balancer_ocid = module.infrastructure.load_balancer_ocid + create_load_balancer = false + load_balancer_ip = module.infrastructure.load_balancer_ip + pool_size = var.vault_pool_size + secret_name = local.secret_name + ocne_secret_name = local.ocne_secret_name + load_balancer_shape = var.load_balancer_shape + enable_bastion = var.enable_bastion + bastion_public_ip = var.enable_bastion ? module.bastion[0].bastion_public_ip : var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key_path = var.enable_bastion || var.bastion_public_ip != "" ? var.bastion_private_key_path : "" + compute_user = var.compute_user + freeform_tags = var.freeform_tags +} + +module "ocne-provision" { + depends_on = [module.infrastructure] + source = "./modules/ocne-provision" + count = var.provision_mode == lookup(local.provision_modes_map, "provision_mode_ocne", "") ? 1 : 0 + + ssh_public_key_path = var.ssh_public_key_path + ssh_private_key_path = var.ssh_private_key_path + os_version = var.os_version + proxy = var.proxy + no_proxy = var.no_proxy + use_vault = var.use_vault + control_plane_node_count = var.control_plane_node_count + worker_node_count = var.worker_node_count + control_plane_nodes = module.infrastructure.control_plane_nodes + worker_nodes = module.infrastructure.worker_nodes + apiserver_ip = module.infrastructure.apiserver_ip + vault_uri = var.use_vault ? module.vault[0].uri : "" + ocne_version = data.external.ocne_config.result.version + environment_name = data.external.ocne_config.result.environment_name + kubernetes_name = data.external.ocne_config.result.kubernetes_name + kube_apiserver_port = var.kube_apiserver_port + kube_apiserver_ip = var.virtual_ip ? module.infrastructure.kube_apiserver_virtual_ip : module.infrastructure.load_balancer_ip + virtual_ip = var.virtual_ip + container_registry = var.container_registry + certificate_signing_token = var.use_vault ? module.vault[0].vault_ocne_client_token : "" + enable_bastion = var.enable_bastion + bastion_public_ip = var.enable_bastion ? module.bastion[0].bastion_public_ip : var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key_path = var.enable_bastion || var.bastion_public_ip != "" ? var.bastion_private_key_path : "" + node_ocids = module.infrastructure.node_ocids + compute_user = var.compute_user + debug = var.debug + restrict_service_externalip_cidrs = var.restrict_service_externalip_cidrs + config_file_path = var.config_file_path + oci_api_key_path = data.external.ocne_config.result.oci_api_key_path + kubevirt_config = data.external.ocne_config.result.kubevirt_config +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/datasources.tf new file mode 100644 index 00000000000..ad5f57c4a57 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/datasources.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +data "oci_core_instance_pool_instances" "pool_instances" { + compartment_id = var.compartment_id + instance_pool_id = oci_core_instance_pool.instance_pool.id +} + +data "oci_core_instance" "instances" { + count = var.pool_size + instance_id = data.oci_core_instance_pool_instances.pool_instances.instances[count.index].id +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/locals.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/locals.tf new file mode 100644 index 00000000000..0148bc2d5a5 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/locals.tf @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +locals { + lb_ocid = var.create_load_balancer ? oci_load_balancer_load_balancer.lb[0].id : var.load_balancer_ocid +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/main.tf new file mode 100644 index 00000000000..90a6c735139 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/main.tf @@ -0,0 +1,101 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +resource "oci_load_balancer_load_balancer" "lb" { + count = var.create_load_balancer ? 1 : 0 + compartment_id = var.compartment_id + display_name = "${var.prefix}-load-balancer" + shape = lookup(var.load_balancer_shape, "shape") + subnet_ids = [var.subnet_id] + is_private = "true" + + # Optional + shape_details { + minimum_bandwidth_in_mbps = lookup(var.load_balancer_shape, "flex_min") + maximum_bandwidth_in_mbps = lookup(var.load_balancer_shape, "flex_max") + } + freeform_tags = var.freeform_tags +} + +resource "oci_load_balancer_backend_set" "lb_backend" { + health_checker { + protocol = var.load_balancer_protocol + port = var.load_balancer_port + url_path = "/sys/health" + } + + load_balancer_id = local.lb_ocid + name = "${var.prefix}-lb-backend" + policy = var.load_balancer_policy +} + +resource "oci_core_instance_configuration" "instance_config" { + compartment_id = var.compartment_id + display_name = "${var.prefix}-instance-configuration" + freeform_tags = var.freeform_tags + + instance_details { + instance_type = "compute" + + launch_details { + availability_domain = var.availability_domain_id + compartment_id = var.compartment_id + display_name = "${var.prefix}-compute" + shape = lookup(var.instance_shape, "shape", "VM.Standard2.2") + + dynamic "shape_config" { + for_each = length(regexall("Flex", lookup(var.instance_shape, "shape", "VM.Standard.E3.Flex"))) > 0 ? [1] : [] + content { + ocpus = max(1, lookup(var.instance_shape, "ocpus", 1)) + memory_in_gbs = (lookup(var.instance_shape, "memory", 4) / lookup(var.instance_shape, "ocpus", 1)) > 64 ? (lookup(var.instance_shape, "ocpus", 1) * 16) : lookup(var.instance_shape, "memory", 4) + } + } + + source_details { + source_type = "image" + image_id = var.image_ocid + boot_volume_size_in_gbs = lookup(var.instance_shape, "boot_volume_size", null) + } + + metadata = { + ssh_authorized_keys = file(var.ssh_public_key_path) + user_data = var.instance_user_data + } + + create_vnic_details { + assign_public_ip = "false" + subnet_id = var.subnet_id + } + } + } +} + +resource "oci_core_instance_pool" "instance_pool" { + depends_on = [oci_load_balancer_backend_set.lb_backend] + instance_configuration_id = oci_core_instance_configuration.instance_config.id + compartment_id = var.compartment_id + display_name = "${var.prefix}-instance-pool" + size = var.pool_size + freeform_tags = var.freeform_tags + + placement_configurations { + availability_domain = var.availability_domain_id + primary_subnet_id = var.subnet_id + } + + load_balancers { + backend_set_name = "${var.prefix}-lb-backend" + load_balancer_id = local.lb_ocid + port = var.load_balancer_port + vnic_selection = "PrimaryVnic" + } +} + +resource "oci_load_balancer_listener" "listener" { + depends_on = [oci_core_instance_pool.instance_pool, oci_load_balancer_backend_set.lb_backend] + default_backend_set_name = oci_core_instance_pool.instance_pool.load_balancers[0].backend_set_name + load_balancer_id = local.lb_ocid + name = "${var.prefix}-listener" + port = var.load_balancer_port + protocol = var.load_balancer_protocol +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/output.tf new file mode 100644 index 00000000000..28b369be860 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/output.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "instances" { + description = "Private IP addresses of instances." + value = data.oci_core_instance.instances.*.private_ip +} + +output "load_balancer_ip" { + description = "The canonical IP address that the load balancer serves traffic from." + value = var.create_load_balancer ? oci_load_balancer_load_balancer.lb[0].ip_address_details[0].ip_address : var.load_balancer_ip +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/variables.tf new file mode 100644 index 00000000000..6261d04f1bc --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/variables.tf @@ -0,0 +1,96 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// Common OCI resource variables +variable "availability_domain_id" { + type = string + description = "The ID of the availability domain inside the `region` in which to create this deployment" +} + +variable "compartment_id" { + type = string + description = "The OCID of the compartment in which to create this deployment" +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "instance_shape" { + type = map(any) + description = "The OCI instance shape to use for all compute resources that are created as part of this deployment" +} + +variable "image_ocid" { + type = string + description = "The OCID of the OS image to use when creating all compute resources that are part of this deployment" +} + +variable "subnet_id" { + type = string + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this variable to set to the empty string, a network configuration will be generated automatically" +} + +// Compute instance specific variables +variable "ssh_public_key_path" { + type = string + description = "The SSH public key path to use when configuring access to any compute resources created as part of this deployment" +} +variable "instance_user_data" { + type = string + description = "Raw cloud-init data that is to be used to instantiate new compute resources" +} + +// Instance pool specific variables +variable "pool_size" { + type = number + description = "The initial number of compute instances to spawn as part of this deployment" +} + +// Load balancer specific variables +variable "load_balancer_shape" { + type = map(string) + description = "The OCI shape of the load balancer to create to service this pool" +} + +variable "load_balancer_policy" { + type = string + description = "The traffic policy to apply to the load balancer backend that represents this pool" + default = "LEAST_CONNECTIONS" +} + +variable "load_balancer_protocol" { + type = string + description = "The protocol that the load balancer listener listens for" + default = "HTTP" +} + +variable "load_balancer_port" { + type = string + description = "The port your application runs on. This port is used to create the load balancer listeners and to create the backends" +} + +variable "load_balancer_ocid" { + type = string + description = "Optional OCID specifying an existing load balancer. If provided, no load balancer will be created and all other resources will be attached to this load balancer" + default = "" +} + +variable "create_load_balancer" { + type = string + description = "If true, automatically generate a load balancer. If false, do not generate one and an OCID must be provided instead" + default = true +} + +variable "load_balancer_ip" { + type = string + description = "If a custom load balancer is provided, this must be set to the canonical IP address that the load balancer serves traffic from" + default = "" +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-autoscaling/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/main.tf new file mode 100644 index 00000000000..732b76a74d1 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/main.tf @@ -0,0 +1,97 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +resource "oci_core_instance" "instance" { + count = var.instance_count + availability_domain = var.availability_domain_id + compartment_id = var.compartment_id + display_name = format("${var.prefix}-%03d", count.index + 1) + freeform_tags = var.freeform_tags + + shape = lookup(var.instance_shape, "shape", "VM.Standard2.2") + + dynamic "shape_config" { + for_each = length(regexall("Flex", lookup(var.instance_shape, "shape", "VM.Standard.E3.Flex"))) > 0 ? [1] : [] + content { + ocpus = max(1, lookup(var.instance_shape, "ocpus", 1)) + memory_in_gbs = (lookup(var.instance_shape, "memory", 4) / lookup(var.instance_shape, "ocpus", 1)) > 64 ? (lookup(var.instance_shape, "ocpus", 1) * 16) : lookup(var.instance_shape, "memory", 4) + } + } + + source_details { + source_type = "image" + source_id = var.image_ocid + boot_volume_size_in_gbs = lookup(var.instance_shape, "boot_volume_size", null) + } + + connection { + agent = false + timeout = "10m" + host = self.private_ip + user = "opc" + private_key = file(var.ssh_private_key_path) + bastion_host = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key = var.enable_bastion || var.bastion_public_ip != "" ? file(var.bastion_private_key_path) : "" + } + + create_vnic_details { + assign_public_ip = "false" + subnet_id = var.subnet_id + hostname_label = format("${var.prefix}-%03d", count.index + 1) + } + + metadata = { + ssh_authorized_keys = file(var.ssh_public_key_path) + } + + provisioner "file" { + content = var.init_script + destination = "/home/${var.compute_user}/ocne-init.sh" + } + + provisioner "remote-exec" { + inline = [ + "set -x", + "sudo bash /home/${var.compute_user}/ocne-init.sh" + ] + } +} + +resource "oci_core_vnic_attachment" "second_vnic" { + count = var.attach_secondary_vnic ? var.instance_count : 0 + instance_id = oci_core_instance.instance[count.index].id + create_vnic_details { + assign_public_ip = "false" + # subnet_id = var.secondary_subnet_id + subnet_id = var.subnet_id + freeform_tags = var.freeform_tags + } +} + +data "oci_core_vnic" "second_vnic" { + count = var.attach_secondary_vnic ? var.instance_count : 0 + vnic_id = oci_core_vnic_attachment.second_vnic[count.index].vnic_id +} + +resource "null_resource" "assign_vnics" { + count = var.attach_secondary_vnic ? var.instance_count : 0 + depends_on = [oci_core_vnic_attachment.second_vnic, data.oci_core_vnic.second_vnic] + + provisioner "remote-exec" { + connection { + agent = false + timeout = "10m" + host = oci_core_instance.instance[count.index].private_ip + user = "opc" + private_key = file(var.ssh_private_key_path) + bastion_host = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key = var.enable_bastion || var.bastion_public_ip != "" ? file(var.bastion_private_key_path) : "" + } + inline = [ + "set -x", + "timeout 10m bash -c \"until (/sbin/ip addr | grep '${data.oci_core_vnic.second_vnic[count.index].private_ip_address}[^0-9]'); do sleep 2; sudo oci-network-config -a; done\"; sudo firewall-cmd --add-interface=ens5; sudo firewall-cmd --zone=public --change-interface=ens5", + ] + } +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/output.tf new file mode 100644 index 00000000000..4774aa65eea --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/output.tf @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "private_ip" { + description = "Private IP address of instance." + value = oci_core_instance.instance.*.private_ip +} + +output "secondary_private_ip" { + depends_on = [null_resource.assign_vnics] + value = data.oci_core_vnic.second_vnic.*.private_ip_address +} + +output "node_ocids" { + description = "Comma separated list of Kubernetes nodes (both control plane and worker nodes) with their Oracle Cloud Identifiers (OCIDs)." + value = oci_core_instance.instance.*.id +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/variables.tf new file mode 100644 index 00000000000..46676ce0d7f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/variables.tf @@ -0,0 +1,98 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// Common OCI resource variables +variable "availability_domain_id" { + type = string + description = "The ID of the availability domain inside the `region` in which to create this deployment" +} + +variable "compartment_id" { + type = string + description = "The OCID of the compartment in which to create this deployment" +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "instance_shape" { + type = map(any) + description = "The OCI instance shape to use for all compute resources that are created as part of this deployment" +} + +variable "image_ocid" { + type = string + description = "The OCID of the OS image to use when creating all compute resources that are part of this deployment" +} + +variable "subnet_id" { + type = string + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this variable to set to the empty string, a network configuration will be generated automatically" +} + +variable "secondary_subnet_id" { + type = string + description = "The OCID of a pre-existing subnet. If non-empty, each instance created by this module will have a secondary VNIC assigned to this subnet and a private IP address within it will be allocated." + default = "" +} + +variable "attach_secondary_vnic" { + type = bool + description = "If set to true, allocate a secondary VNIC and attach it to the subnet identified bvy `secondary_subnet_id`" + default = false +} + +// Compute instance specific variables +variable "ssh_public_key_path" { + type = string + description = "The SSH public key path to use when configuring access to any compute resources created as part of this deployment" +} + +variable "ssh_private_key_path" { + type = string + description = "THe SSH private key path that goes with the SSH public key that is used when accessing compute resources that are created as part of this deployment" +} + +variable "init_script" { + type = string + description = "Init script post instance creation" +} + +variable "instance_count" { + type = number + description = "The number of compute instances to create with the given configuration" +} + +variable "enable_bastion" { + type = bool + description = "Decides if bastion is installed. Intended for internal use. Set to false." +} + +variable "bastion_public_ip" { + type = string + description = "Public IP address of the Bastion host" +} + +variable "bastion_user" { + type = string + description = "User name on the Bastion host" + default = "opc" +} + +variable "bastion_private_key_path" { + type = string + description = "The location of the SSH private key for the Bastion host" +} + +variable "compute_user" { + type = string + description = "A user configured with sudo access and authenticated with ssh_public_key_path and ssh_private_key_path on each compute resource" +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-compute/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf new file mode 100644 index 00000000000..ad046dfb785 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf @@ -0,0 +1,20 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# Gets the OCID of the OS image to use +data "oci_core_images" "OLImageOCID" { + compartment_id = var.compartment_id + #operating_system = var.os + #operating_system_version = var.os_version + #sort_by = "TIMECREATED" + #sort_order = "DESC" + + display_name = "Oracle-Linux-8.10-2024.08.29-0" + #display_name = "Oracle-Linux-8.10-2024.09.30-0" + # filter to avoid Oracle Linux images for GPU + #filter { + # name = "display_name" + # values = ["^${replace(var.os, " ", "-")}-${var.os_version}\\.?[0-9]?-[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]-[0-9]$"] + # regex = true + #} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/output.tf new file mode 100644 index 00000000000..d7c6f0da23f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/output.tf @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "image_ocid" { + value = data.oci_core_images.OLImageOCID.images[0].id +} + +output "all_images" { + value = data.oci_core_images.OLImageOCID.images +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/variables.tf new file mode 100644 index 00000000000..b862228ebad --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/variables.tf @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +variable "compartment_id" { +} + +variable "os" { + default = "Oracle Linux" +} + +variable "os_version" { + default = "8" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/datasources.tf new file mode 100644 index 00000000000..59259b9e083 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/datasources.tf @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +data "oci_kms_vault" "vault" { + count = length(var.vault_ocid) > 0 ? 1 : 0 + vault_id = var.vault_ocid +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/locals.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/locals.tf new file mode 100644 index 00000000000..181de50b317 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/locals.tf @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +locals { + vault_ocid = length(var.vault_ocid) > 0 ? data.oci_kms_vault.vault[0].id : oci_kms_vault.vault[0].id + key_ocid = length(var.key_ocid) > 0 ? var.key_ocid : oci_kms_key.vault_key[0].id + crypto_endpoint = length(var.vault_ocid) > 0 ? data.oci_kms_vault.vault[0].crypto_endpoint : oci_kms_vault.vault[0].crypto_endpoint + management_endpoint = length(var.vault_ocid) > 0 ? data.oci_kms_vault.vault[0].management_endpoint : oci_kms_vault.vault[0].management_endpoint +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/main.tf new file mode 100644 index 00000000000..e8f71f2a08b --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/main.tf @@ -0,0 +1,27 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +resource "oci_kms_vault" "vault" { + count = length(var.vault_ocid) > 0 ? 0 : 1 + compartment_id = var.compartment_id + display_name = "${var.prefix}-vault" + vault_type = var.type + + # Optional + freeform_tags = var.freeform_tags +} + +resource "oci_kms_key" "vault_key" { + count = length(var.key_ocid) > 0 ? 0 : 1 + compartment_id = var.compartment_id + display_name = "${var.prefix}-vault-key" + key_shape { + algorithm = var.key_algorithm + length = var.key_length + } + management_endpoint = local.management_endpoint + + # Optional + freeform_tags = var.freeform_tags +} + diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/output.tf new file mode 100644 index 00000000000..0503101282e --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/output.tf @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "management_endpoint" { + description = "OCI KMS vault management endpoint." + value = local.management_endpoint +} + +output "crypto_endpoint" { + description = "OCI KMS vault crypto endpoint." + value = local.crypto_endpoint +} + +output "key_id" { + description = "OCI KMS vault key id." + value = local.key_ocid +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/variables.tf new file mode 100644 index 00000000000..4321f3acf87 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/variables.tf @@ -0,0 +1,47 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// Common OCI resource variables +variable "compartment_id" { + type = string + description = "The OCID of the compartment in which to create this deployment" +} +variable "vault_ocid" { + type = string + description = "The OCID of the OCI KMS Vault to use. If not provided, one will be created" +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "type" { + type = string + description = "The type of OCI KMS Vault to instantiate" + default = "DEFAULT" +} + +variable "key_length" { + type = number + description = "The length of the OCI KMS Vault Key to generate, in bits" + default = "16" +} + +variable "key_algorithm" { + type = string + description = "The cryptographic algorithm used to generate the OCI KMS Vault Key" + default = "AES" +} + +variable "key_ocid" { + type = string + description = "The OCID of the OCI KMS Vault Key to use. If not provided, one will be created" + default = "" +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-kms/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/main.tf new file mode 100644 index 00000000000..93aae04483d --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/main.tf @@ -0,0 +1,55 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +resource "oci_load_balancer_load_balancer" "kube_apiserver_lb" { + compartment_id = var.compartment_id + display_name = "${var.prefix}-lb" + shape = lookup(var.shape, "shape") + subnet_ids = [var.subnet_id] + is_private = "true" + count = var.instance_count + + # Optional + dynamic "shape_details" { + for_each = length(regexall("flexible", lookup(var.shape, "shape", "flexible"))) > 0 ? [1] : [] + content { + minimum_bandwidth_in_mbps = lookup(var.shape, "flex_min") + maximum_bandwidth_in_mbps = lookup(var.shape, "flex_max") + } + } + freeform_tags = var.freeform_tags +} + +resource "oci_load_balancer_backend_set" "lb_backend" { + health_checker { + protocol = var.protocol + port = var.port + url_path = "/sys/health" + } + count = var.instance_count + load_balancer_id = oci_load_balancer_load_balancer.kube_apiserver_lb[0].id + name = "${var.prefix}-backend" + policy = var.policy +} + +// Please see the comment on the backend_count variable to +// understand why this is done with count rather than for_each +// and why there's a stupid lookup into backends variables. +resource "oci_load_balancer_backend" "backends" { + depends_on = [oci_load_balancer_backend_set.lb_backend] + count = (var.instance_count > 0) ? var.backend_count : 0 + backendset_name = "${var.prefix}-backend" + ip_address = keys(var.backends)[count.index] + load_balancer_id = oci_load_balancer_load_balancer.kube_apiserver_lb[0].id + port = var.backends[keys(var.backends)[count.index]] +} + +resource "oci_load_balancer_listener" "listener" { + depends_on = [oci_load_balancer_backend_set.lb_backend] + default_backend_set_name = "${var.prefix}-backend" + load_balancer_id = oci_load_balancer_load_balancer.kube_apiserver_lb[0].id + name = "${var.prefix}-listener" + port = var.port + protocol = var.protocol + count = var.instance_count +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/output.tf new file mode 100644 index 00000000000..56e647a07b2 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/output.tf @@ -0,0 +1,23 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "ip_address" { + description = "IP address of load balancer." + value = var.instance_count > 0 ? oci_load_balancer_load_balancer.kube_apiserver_lb[0].ip_address_details[0].ip_address: "" +} + +output "port" { + description = "The port that the listener should serve traffic on" + value = var.port +} + +output "endpoint" { + depends_on = [oci_load_balancer_listener.listener, oci_load_balancer_backend.backends] + description = "Load balancer URI." + value = var.instance_count > 0 ? "${oci_load_balancer_load_balancer.kube_apiserver_lb[0].ip_address_details[0].ip_address}:${var.port}" : "" +} + +output "load_balancer_ocid" { + description = "OCID of the load balancer." + value = var.instance_count > 0 ? oci_load_balancer_load_balancer.kube_apiserver_lb[0].id : "" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/variables.tf new file mode 100644 index 00000000000..a3f5d45a4a9 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/variables.tf @@ -0,0 +1,69 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// Common OCI resource variables +variable "compartment_id" { + type = string + description = "The OCID of the compartment in which to create this deployment" +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "subnet_id" { + type = string + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this variable to set to the empty string, a network configuration will be generated automatically" +} + +// Load balancer specific variables +variable "shape" { + type = map(string) + description = "The OCI shape of the load balancer to create" +} + +variable "policy" { + type = string + description = "The traffic policy to apply to the load balancer backend set" + default = "LEAST_CONNECTIONS" +} + +variable "protocol" { + type = string + description = "The protocol that the listener create by this module listens on" + default = "HTTP" +} + +variable "port" { + type = number + description = "The port that the listener created by this module should serve traffic on" +} + +variable "backends" { + type = map(any) + description = "A mapping of IP address or hostname to ports. These will become the backend set for the load balancer" +} + +// While in most cases the length of the backend +// map can be calculated appropriately, there are +// cases where the backends are dynamically generated +// by some calling module where the plan stage would +// be unable determine the backends variable until +// part of the apply has finished. Hence this +// seemingly pointless argument. +variable "backend_count" { + type = number + description = "The number of keys in the `backends` variable" +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} + +variable "instance_count" { + type = number + description = "The number of LB instances to create with the given configuration" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-loadbalancer/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/main.tf new file mode 100644 index 00000000000..1398e048dfb --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/main.tf @@ -0,0 +1,127 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# Modules and Resources +module "vcn" { + source = "oracle-terraform-modules/vcn/oci" + version = "3.5.4" + count = var.deploy_networking ? 1 : 0 + + # Required + compartment_id = var.compartment_id + + # Optional + vcn_name = "${var.prefix}-vcn" + vcn_dns_label = var.vcn_dns_label + create_internet_gateway = true + create_nat_gateway = true + create_service_gateway = false + vcn_cidrs = [var.vnc_cidr_block] + freeform_tags = var.freeform_tags +} + +resource "oci_core_subnet" "tf_vcn_private_subnet" { + count = var.deploy_networking ? 1 : 0 + + # Required + compartment_id = var.compartment_id + vcn_id = var.deploy_networking ? module.vcn[0].vcn_id : var.vcn_id + cidr_block = var.vnc_private_subnet_cidr_block + + # Optional + route_table_id = var.deploy_networking ? module.vcn[0].nat_route_id : var.nat_route_id + dns_label = var.private_dns_label + dhcp_options_id = oci_core_dhcp_options.tf_dhcp_options[0].id + security_list_ids = [oci_core_security_list.tf_private_security_list[0].id] + display_name = "${var.prefix}-private-subnet" + freeform_tags = var.freeform_tags +} + +resource "oci_core_security_list" "tf_private_security_list" { + count = var.deploy_networking ? 1 : 0 + + compartment_id = var.compartment_id + vcn_id = var.deploy_networking ? module.vcn[0].vcn_id : var.vcn_id + display_name = "${var.prefix}-sg-private-subnet" + + freeform_tags = var.freeform_tags + + egress_security_rules { + stateless = false + destination = "0.0.0.0/0" + destination_type = "CIDR_BLOCK" + protocol = "all" + } + + ingress_security_rules { + stateless = false + source = var.vnc_cidr_block + source_type = "CIDR_BLOCK" + + # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml TCP is 6 + protocol = "6" + tcp_options { + min = 1 + max = 65535 + } + } + + ingress_security_rules { + stateless = false + source = var.vnc_cidr_block + source_type = "CIDR_BLOCK" + + # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml UDP is 17 + protocol = "17" + udp_options { + min = 1 + max = 65535 + } + } + + ingress_security_rules { + stateless = false + source = "0.0.0.0/0" + source_type = "CIDR_BLOCK" + # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml ICMP is 1 + protocol = "1" + + # For ICMP type and code see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml + icmp_options { + type = 3 + code = 4 + } + } + + ingress_security_rules { + stateless = false + source = var.vnc_cidr_block + source_type = "CIDR_BLOCK" + # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml ICMP is 1 + protocol = "1" + + # For ICMP type and code see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml + icmp_options { + type = 3 + } + } +} + +resource "oci_core_dhcp_options" "tf_dhcp_options" { + count = var.deploy_networking ? 1 : 0 + + # Required + compartment_id = var.compartment_id + vcn_id = var.deploy_networking ? module.vcn[0].vcn_id : var.vcn_id + + #Options for type are either "DomainNameServer" or "SearchDomain" + options { + type = "DomainNameServer" + server_type = "VcnLocalPlusInternet" + } + + # Optional + display_name = "ocne-dhcp-options" + freeform_tags = var.freeform_tags +} + diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/output.tf new file mode 100644 index 00000000000..d129c5c6b2b --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/output.tf @@ -0,0 +1,19 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# Outputs +output "vcn_id" { + value = var.deploy_networking ? module.vcn[0].vcn_id : var.vcn_id +} + +output "ig_route_id" { + value = var.deploy_networking ? module.vcn[0].ig_route_id : var.ig_route_id +} + +output "private_security_list_id" { + value = var.deploy_networking ? oci_core_security_list.tf_private_security_list[*] : [] +} + +output "private_subnet_id" { + value = var.deploy_networking ? oci_core_subnet.tf_vcn_private_subnet[*] : [] +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/variables.tf new file mode 100644 index 00000000000..3ce95b4db42 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/variables.tf @@ -0,0 +1,70 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# Variables +variable "compartment_id" { + type = string +} + +variable "prefix" { + type = string +} + +variable "private_dns_label" { + type = string + default = "private" +} + +variable "ssh_public_key_path" { + type = string +} + +variable "vcn_dns_label" { + type = string + default = "ocne" +} + +variable "vnc_cidr_block" { + type = string + default = "10.0.0.0/16" +} + +variable "vnc_private_subnet_cidr_block" { + type = string + default = "10.0.0.0/24" +} + +variable "vcn_id" { + type = string + description = "The OCID of the OCI Virtual Cloud Network in which to create any subnets that might be generated as part of this deployment" +} + +variable "ig_route_id" { + type = string +} + +variable "nat_route_id" { + type = string +} + +variable "deploy_networking" { + type = bool + description = "Decides if VCN is installed." +} + +variable "enable_bastion" { + type = bool + description = "Decides if bastion is installed. Intended for internal use. Set to false." +} + +variable "enable_notification" { + description = "Whether to enable ONS notification for the bastion host." + default = false + type = bool +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/versions.tf new file mode 100644 index 00000000000..b3b726a82c1 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-network/versions.tf @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/config-file.yaml.example b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/config-file.yaml.example new file mode 100644 index 00000000000..ba1c879f745 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/config-file.yaml.example @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. +# Licensed under the GNU General Public License Version 3 as shown at https://www.gnu.org/licenses/gpl-3.0.txt. + +environments: + - environment-name: myenvironment + globals: + version: 1.7 + modules: + - module: kubernetes + name: mycluster + args: + restrict-service-externalip: false diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/datasources.tf new file mode 100644 index 00000000000..8ec71e1b855 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/datasources.tf @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +data "template_file" "provision" { + template = file("${path.module}/files/provision.template.sh") + vars = { + debug = var.debug + } +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-edit.sh b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-edit.sh new file mode 100755 index 00000000000..51245673ff0 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-edit.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. +# Licensed under the GNU General Public License Version 3 as shown at https://www.gnu.org/licenses/gpl-3.0.txt. + +inpFile=${1} +envName=${2} +k8sName=${3} +ocneApi=${4} +k8sApis=${5} +k8sPort=${6} +virtual=${7} +cpNodes=${8} +wkNodes=${9} + proxy=${10} +noProxy=${11} +containerRegistry=${12} +ociUser=${13} +ocneVer=${14} + +function list_mods() { + cat ${inpFile} | yq eval '.environments[0].modules[].module' +} + +function indexOf() { + local mods=($(list_mods)) + local idx=-1 + local found=false + for mod in "${mods[@]}"; do + idx=$(($idx + 1)) + if [[ "${mod}" == "$1" ]]; then + found=true + echo $idx + fi + done + if [[ "${found}" == "false" ]]; then + echo -1 + fi +} + +function setEnv() { + yq e ".environments[0].globals.\"${1}\" |= \"${2}\"" -i ${inpFile} +} + +function addEnv() { + local cur=$(getEnv ${1}) + if [[ "${cur}" == "" || "${cur}" == "null" ]]; then + setEnv ${1} "${2}" + fi +} + +function getEnv() { + yq e ".environments[0].globals.\"${1}\"" ${inpFile} +} + +function getArg() { + local idx=$(indexOf ${1}) + yq e ".environments[0].modules[\"${idx}\"].args.\"${2}\"" ${inpFile} +} + +# Default the arg value if it is not set +function defArg() { + local idx=$(indexOf ${1}) + local cur=$(getArg ${1} ${2}) + if [[ "${cur}" == "" || "${cur}" == "null" ]]; then + yq e ".environments[0].modules[\"${idx}\"].args.\"${2}\" |= \"${3}\"" -i ${inpFile} + fi +} + +# Set the arg value +function setArg() { + local idx=$(indexOf ${1}) + yq e ".environments[0].modules[\"${idx}\"].args.\"${2}\" |= \"${3}\"" -i ${inpFile} +} + +function addArg() { + local idx=$(indexOf ${1}) + yq e ".environments[0].modules[\"${idx}\"].args.\"${2}\" += [\"${3}\"]" -i ${inpFile} + # cat ${inpFile} +} + +function wPort() { + if [[ ${1} == *":${2}" ]]; then + echo "${1}" + else + echo "${1}:${2}" + fi +} + +yq e ".environments[0].environment-name |= \"${envName}\"" -i ${inpFile} +ocneApi=$(wPort ${ocneApi} "8091") +setEnv api-server ${ocneApi} +if [[ "${virtual}" == "true" ]]; then + setEnv virtual-ip ${k8sApis} +else + k8sApis=$(wPort ${k8sApis} ${k8sPort}) + setEnv load-balancer ${k8sApis} +fi +addEnv remote-command "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" +if [[ "${ocneVer}" != "" ]]; then + curVer=$(getEnv version) + if [[ "${curVer}" == "" || "${curVer}" == "null" || "${curVer}" == "1.7" ]]; then + setEnv version "${ocneVer}" + fi +fi +if [[ "${proxy}" != "" ]]; then + addEnv https-proxy ${proxy} + addEnv http-proxy ${proxy} +fi +if [[ "${noProxy}" != "" ]]; then + addEnv no-proxy ${noProxy} +fi + +indexOfHelm=$(indexOf helm) +indexOfKube=$(indexOf kubernetes) + +ociKeyPath=$(getArg oci-ccm oci-private-key-file) +if [ ${ociKeyPath} != null ]; then + setArg oci-ccm oci-private-key-file "/home/${ociUser}/oci_api_key.pem" +fi +kvrtConfig=$(getArg kubevirt kubevirt-config) +if [ ${kvrtConfig} != null ]; then + setArg kubevirt kubevirt-config "/home/${ociUser}/kubevirt_config.yaml" +fi + +yq e ".environments[0].modules[\"${indexOfKube}\"].name |= \"${k8sName}\"" -i ${inpFile} +defArg kubernetes "container-registry" ${containerRegistry} + +list=(${cpNodes//,/ }) +for item in "${list[@]}"; do + item=$(wPort ${item} "8090") + if [[ "${ocneVer}" == "1.5."* ]] || [[ "${ocneVer}" == "1.5" ]]; then + addArg kubernetes master-nodes ${item} + else + addArg kubernetes control-plane-nodes ${item} + fi +done + +list=(${wkNodes//,/ }) +for item in "${list[@]}"; do + item=$(wPort ${item} "8090") + addArg kubernetes worker-nodes ${item} +done + +cat ${inpFile} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-read.sh b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-read.sh new file mode 100755 index 00000000000..70fe367446f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/ocne-provision/files/config-read.sh @@ -0,0 +1,139 @@ +#!/bin/bash + +# Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. +# Licensed under the GNU General Public License Version 3 as shown at https://www.gnu.org/licenses/gpl-3.0.txt. + +set -e +eval "$(jq -r '@sh "envName=\(.en) k8sName=\(.kn) ocneVer=\(.ov) cfgFile=\(.path) ERR=\(.error)"')" + +ERR="" +function addErr() { + if [ "${ERR}" == "" ]; then + cat - </dev/null 2>&1 ; then + version=$(yq --version | awk '{print $NF}' | sed 's/^v//') + oldver=$(printf "${version}"'\n'"${yqMin}"'\n' | sort -V | head -n1) + if [[ "${oldver}" != "${yqMin}" ]]; then + ERR=`addErr "Require yq >= v${yqMin}"` + fi +else + # echo "yq is not installed" >> debug.log + ERR=`addErr "Require yq >= v${yqMin}"` +fi + +function list_mods() { + cat ${cfgFile} | yq eval '.environments[0].modules[].module' +} + +function indexOf() { + local mods=($(list_mods)) + local idx=-1 + local found=false + for mod in "${mods[@]}"; do + idx=$(($idx + 1)) + if [ "${mod}" == "$1" ]; then + found=true + echo $idx + fi + done + if [ "${found}" == "false" ]; then + echo -1 + fi +} + +function nameOf() { + yq e ".environments[0].modules[\"${1}\"].name" ${cfgFile} +} + +function getArg() { + local idx=$(indexOf ${1}) + yq e ".environments[0].modules[\"${idx}\"].args.\"${2}\"" ${cfgFile} +} + +function getFilePath() { + filePath=$(getArg ${1} ${2}) + if [ ${filePath} == null ]; then + filePath="" + else + if [ -f "${filePath}" ]; then + filePath="${filePath}" + else + filePath="The file of ${2} ${filePath} does not exist" + fi + fi + echo "${filePath}" +} + +ociKeyPath="" +kvrtConfig="" +if [[ -e ${cfgFile} ]]; then + if [ "${envName}" != "myenvironment" ] && [ "${envName}" != "" ]; then + ERR=`addErr "Remove variable environment_name=${envName} from terraform.tfvars"` + fi + if [ "${k8sName}" != "mycluster" ] && [ "${k8sName}" != "" ]; then + ERR=`addErr "Remove variable kubernetes_name=${k8sName} from terraform.tfvars"` + fi + if [ "${ocneVer}" != "1.7" ] && [ "${ocneVer}" != "" ]; then + ERR=`addErr "Remove variable ocne_version=${ocneVer} from terraform.tfvars"` + fi + numOfEnv=`yq eval '.environments | length' ${cfgFile}` + if [[ "${numOfEnv}" != "1" ]]; then + ERR=`addErr "config-file ${cfgFile} must have ONE environment"` + fi + mods=($(list_mods)) + findK="" + for mod in "${mods[@]}"; do + if [ "${mod}" == "kubernetes" ]; then + if [ "${findK}" != "" ]; then + ERR=`addErr "config-file ${cfgFile} must have ONE kubernetes module"` + fi + findK="kubernetes" + fi + done + if [ "${findK}" == "" ]; then + ERR=`addErr "config-file ${cfgFile} must have ONE kubernetes module"` + fi + indexOfKube=$(indexOf kubernetes) + envName=`yq eval '.environments[0].environment-name' ${cfgFile}` + k8sName=$(nameOf ${indexOfKube}) + ocneVer=`yq eval '.environments[0].globals.version' ${cfgFile}` + if [ ${ocneVer} == null ]; then + ocneVer="" + fi + ociKeyPath=$(getFilePath oci-ccm oci-private-key-file) + if [[ "${ociKeyPath}" == *"does not exist" ]]; then + ERR=`addErr "${ociKeyPath}"` + ociKeyPath="" + fi + kvrtConfig=$(getFilePath kubevirt kubevirt-config) + if [[ "${kvrtConfig}" == *"does not exist" ]]; then + ERR=`addErr "${kvrtConfig}"` + kvrtConfig="" + fi +else + if [[ "${cfgFile}" != "" ]]; then + ERR=`addErr "The file of config_file_path ${cfgFile} does not exist"` + fi +fi + +# echo "config-read ERR: ${ERR}" >> debug.log +yq -o json < 0 ? [1] : [] + content { + ocpus = max(1, lookup(var.bastion_shape, "ocpus", 1)) + memory_in_gbs = (lookup(var.bastion_shape, "memory", 4) / lookup(var.bastion_shape, "ocpus", 1)) > 64 ? (lookup(var.bastion_shape, "ocpus", 1) * 4) : lookup(var.bastion_shape, "memory", 4) + } + } + + source_details { + boot_volume_size_in_gbs = lookup(var.bastion_shape, "boot_volume_size", null) + source_type = "image" + source_id = local.bastion_image_id + } + + timeouts { + create = "60m" + } +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/datasources.tf new file mode 100644 index 00000000000..aac2d671458 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/datasources.tf @@ -0,0 +1,93 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +data "oci_identity_availability_domains" "ad_list" { + compartment_id = var.tenancy_id +} + +data "template_file" "ad_names" { + count = length(data.oci_identity_availability_domains.ad_list.availability_domains) + template = lookup(data.oci_identity_availability_domains.ad_list.availability_domains[count.index], "name") +} + +data "oci_identity_tenancy" "tenancy" { + tenancy_id = var.tenancy_id +} + +# get the tenancy's home region +data "oci_identity_regions" "home_region" { + filter { + name = "key" + values = [data.oci_identity_tenancy.tenancy.home_region_key] + } +} + +data "oci_core_vcn" "vcn" { + vcn_id = var.vcn_id +} + +data "template_file" "autonomous_template" { + template = file("${path.module}/scripts/notification.template.sh") + + vars = { + enable_notification = var.enable_notification + topic_id = var.enable_notification == true ? oci_ons_notification_topic.bastion_notification[0].topic_id : "null" + } + + count = var.bastion_image_id == "Autonomous" ? 1 : 0 +} + +data "template_file" "autonomous_cloud_init_file" { + template = file("${path.module}/cloudinit/autonomous.template.yaml") + + vars = { + notification_sh_content = base64gzip(data.template_file.autonomous_template[0].rendered) + timezone = var.timezone + } + + count = var.bastion_image_id == "Autonomous" ? 1 : 0 +} + +data "oci_core_images" "autonomous_images" { + compartment_id = var.compartment_id + operating_system = "Oracle Autonomous Linux" + shape = lookup(var.bastion_shape, "shape", "VM.Standard.E2.2") + sort_by = "TIMECREATED" +} + +# cloud init for bastion +data "template_cloudinit_config" "bastion" { + gzip = true + base64_encode = true + + part { + filename = "bastion.yaml" + content_type = "text/cloud-config" + content = data.template_file.autonomous_cloud_init_file[0].rendered + } +} + +# Gets a list of VNIC attachments on the bastion instance +data "oci_core_vnic_attachments" "bastion_vnics_attachments" { + availability_domain = element(local.ad_names, (var.availability_domain - 1)) + compartment_id = var.compartment_id + depends_on = [oci_core_instance.bastion] + instance_id = oci_core_instance.bastion.id +} + +# Gets the OCID of the first (default) VNIC on the bastion instance +data "oci_core_vnic" "bastion_vnic" { + depends_on = [oci_core_instance.bastion] + vnic_id = lookup(data.oci_core_vnic_attachments.bastion_vnics_attachments.vnic_attachments[0], "vnic_id") +} + +data "oci_core_instance" "bastion" { + depends_on = [oci_core_instance.bastion] + instance_id = oci_core_instance.bastion.id +} + +data "oci_ons_notification_topic" "bastion_notification" { + topic_id = oci_ons_notification_topic.bastion_notification[0].topic_id + + count = var.enable_notification ? 1 : 0 +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/locals.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/locals.tf new file mode 100644 index 00000000000..ead7657a9c8 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/locals.tf @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +# Protocols are specified as protocol numbers. +# https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml + +locals { + all_protocols = "all" + ad_names = data.template_file.ad_names.*.rendered + anywhere = "0.0.0.0/0" + ssh_port = 22 + tcp_protocol = 6 + bastion_image_id = var.bastion_image_id == "Autonomous" ? data.oci_core_images.autonomous_images.images.0.id : var.bastion_image_id + vcn_cidr = data.oci_core_vcn.vcn.cidr_block +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/ons.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/ons.tf new file mode 100644 index 00000000000..cbaf97f0d5f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/ons.tf @@ -0,0 +1,46 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +resource "oci_ons_notification_topic" "bastion_notification" { + compartment_id = var.compartment_id + name = var.prefix == "none" ? var.notification_topic : "${var.prefix}-${var.notification_topic}" + + count = var.enable_notification ? 1 : 0 + + freeform_tags = var.freeform_tags +} + +resource "oci_ons_subscription" "bastion_notification" { + compartment_id = var.compartment_id + endpoint = var.notification_endpoint + protocol = var.notification_protocol + topic_id = oci_ons_notification_topic.bastion_notification[0].topic_id + + count = var.enable_notification ? 1 : 0 + + freeform_tags = var.freeform_tags +} + +resource "oci_identity_dynamic_group" "bastion_notification" { + compartment_id = var.tenancy_id + depends_on = [oci_core_instance.bastion] + description = "dynamic group to allow bastion to send notifications to ONS" + matching_rule = "ALL {instance.id = '${join(",", data.oci_core_instance.bastion.*.id)}'}" + name = var.prefix == "none" ? "bastion-notification" : "${var.prefix}-bastion-notification" + + count = var.enable_notification ? 1 : 0 + + freeform_tags = var.freeform_tags +} + +resource "oci_identity_policy" "bastion_notification" { + compartment_id = var.compartment_id + depends_on = [oci_core_instance.bastion] + description = "policy to allow bastion host to publish messages to ONS" + name = var.prefix == "none" ? "bastion-notification" : "${var.prefix}-bastion-notification" + statements = ["Allow dynamic-group ${oci_identity_dynamic_group.bastion_notification[0].name} to use ons-topic in compartment id ${var.compartment_id} where request.permission='ONS_TOPIC_PUBLISH'"] + + count = var.enable_notification ? 1 : 0 + + freeform_tags = var.freeform_tags +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/outputs.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/outputs.tf new file mode 100644 index 00000000000..bd5fdf8cb7a --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/outputs.tf @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "bastion_public_ip" { + value = join(",", data.oci_core_vnic.bastion_vnic.*.public_ip_address) +} \ No newline at end of file diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/scripts/notification.template.sh b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/scripts/notification.template.sh new file mode 100644 index 00000000000..6715d063107 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/scripts/notification.template.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +if [ ${enable_notification} ]; then + sudo al-config -T ${topic_id} +else + echo 'ONS notification not enabled' +fi \ No newline at end of file diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/security.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/security.tf new file mode 100644 index 00000000000..d0558a43e98 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/security.tf @@ -0,0 +1,25 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +resource "oci_core_security_list" "bastion" { + compartment_id = var.compartment_id + display_name = var.prefix == "none" ? "bastion" : "${var.prefix}-bastion" + freeform_tags = var.freeform_tags + + egress_security_rules { + protocol = local.all_protocols + destination = local.anywhere + } + + ingress_security_rules { + # allow ssh + protocol = local.tcp_protocol + source = var.bastion_access == "ANYWHERE" ? local.anywhere : var.bastion_access + + tcp_options { + min = local.ssh_port + max = local.ssh_port + } + } + vcn_id = var.vcn_id +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/subnets.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/subnets.tf new file mode 100644 index 00000000000..23678f9be13 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/subnets.tf @@ -0,0 +1,14 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + +resource "oci_core_subnet" "bastion" { + cidr_block = cidrsubnet(local.vcn_cidr, var.newbits, var.netnum) + compartment_id = var.compartment_id + display_name = var.prefix == "none" ? "bastion" : "${var.prefix}-bastion" + dns_label = "bastion" + freeform_tags = var.freeform_tags + prohibit_public_ip_on_vnic = false + route_table_id = var.ig_route_id + security_list_ids = [oci_core_security_list.bastion.id] + vcn_id = var.vcn_id +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/variables.tf new file mode 100644 index 00000000000..a4f93837dc4 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/variables.tf @@ -0,0 +1,127 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# provider identity parameters +variable "tenancy_id" { + description = "tenancy id where to create the sources" + type = string + default = "" +} + +# general oci parameters + +variable "compartment_id" { + description = "compartment id where to create all resources" + type = string +} + +variable "prefix" { + description = "a string that will be prepended to all resources" + type = string + default = "none" +} + +# network parameters + +variable "availability_domain" { + description = "the AD to place the bastion host" + default = 1 + type = number +} + +variable "bastion_access" { + description = "CIDR block in the form of a string to which ssh access to the bastion must be restricted to. *_ANYWHERE_* is equivalent to 0.0.0.0/0 and allows ssh access from anywhere." + default = "ANYWHERE" + type = string +} + +variable "ig_route_id" { + description = "the route id to the internet gateway" + type = string +} + +variable "netnum" { + description = "0-based index of the bastion subnet when the VCN's CIDR is masked with the corresponding newbit value." + default = 32 + type = number +} + +variable "newbits" { + description = "The difference between the VCN's netmask and the desired bastion subnet mask" + default = 13 + type = number +} + +variable "vcn_id" { + description = "The id of the VCN to use when creating the bastion resources." + type = string +} + +# bastion host parameters + +variable "bastion_image_id" { + description = "Provide a custom image id for the bastion host or leave as Autonomous." + default = "Autonomous" + type = string +} + +variable "bastion_shape" { + description = "The shape of bastion instance." + type = map(any) +} + +variable "bastion_upgrade" { + description = "Whether to upgrade the bastion host packages after provisioning. It's useful to set this to false during development/testing so the bastion is provisioned faster." + default = false + type = bool +} + +variable "ssh_public_key" { + description = "the content of the ssh public key used to access the bastion. set this or the ssh_public_key_path" + default = "" + type = string +} + +variable "ssh_public_key_path" { + description = "path to the ssh public key used to access the bastion. set this or the ssh_public_key" + default = "" + type = string +} + +variable "timezone" { + description = "The preferred timezone for the bastion host." + default = "Australia/Sydney" + type = string +} + +# bastion notification + +variable "enable_notification" { + description = "Whether to enable ONS notification for the bastion host." + type = bool +} + +variable "notification_endpoint" { + description = "The subscription notification endpoint. Email address to be notified." + default = null + type = string +} + +variable "notification_protocol" { + description = "The notification protocol used." + default = "EMAIL" + type = string +} + +variable "notification_topic" { + description = "The name of the notification topic" + default = "bastion" + type = string +} + +# tagging +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-bastion/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/agent_init.sh b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/agent_init.sh new file mode 100644 index 00000000000..5c4d286016f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/agent_init.sh @@ -0,0 +1,18 @@ +#! /bin/bash + +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +sudo dd iflag=direct if=/dev/oracleoci/oraclevda of=/dev/null count=1 +echo "1" | sudo tee /sys/class/block/`readlink /dev/oracleoci/oraclevda | cut -d'/' -f 2`/device/rescan +sudo /usr/libexec/oci-growfs -y + +if [[ ${os_version} > 7.9 ]]; then + if [[ ${yum_repo_url} != *"yum.oracle.com"* ]]; then + dnf config-manager --add-repo ${yum_repo_url} + fi +else + if [[ ${yum_repo_url} != *"yum.oracle.com"* ]]; then + yum-config-manager --add-repo ${yum_repo_url} + fi +fi diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/apiserver_init.sh b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/apiserver_init.sh new file mode 100644 index 00000000000..2b9d26984d1 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/files/apiserver_init.sh @@ -0,0 +1,65 @@ +#! /bin/bash + +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +sudo dd iflag=direct if=/dev/oracleoci/oraclevda of=/dev/null count=1 +echo "1" | sudo tee /sys/class/block/`readlink /dev/oracleoci/oraclevda | cut -d'/' -f 2`/device/rescan +sudo /usr/libexec/oci-growfs -y + +if [[ ${os_version} > 7.9 ]]; then + if [[ ${yum_repo_url} != *"yum.oracle.com"* ]]; then + dnf config-manager --add-repo ${yum_repo_url} + fi +else + if [[ ${yum_repo_url} != *"yum.oracle.com"* ]]; then + yum-config-manager --add-repo ${yum_repo_url} + fi +fi + +function getPackageSuffix() { + packageSuffix="${ocne_version}" + if [[ $(echo $${packageSuffix} | awk -F'.' '{print NF}') -lt 3 ]]; then + packageSuffix="" + fi + if [[ $(echo $${packageSuffix} | awk -F'-' '{print NF}') -gt 1 ]]; then + if [[ "$${packageSuffix}" != *".el7" ]] && [[ "$${packageSuffix}" != *".el8" ]] && [[ "$${packageSuffix}" != *".el9" ]] ; then + if [[ $(cat /etc/oracle-release) == "Oracle Linux Server release 9."* ]]; then + packageSuffix="$${packageSuffix}.el9" + elif [[ $(cat /etc/oracle-release) == "Oracle Linux Server release 8."* ]]; then + packageSuffix="$${packageSuffix}.el8" + else + packageSuffix="$${packageSuffix}.el7" + fi + fi + fi + if [[ "$${packageSuffix}" != "" ]]; then + packageSuffix="-$${packageSuffix}" + fi + echo $${packageSuffix} +} + +# install olcnectl on api-server +if [[ ${os_version} > 8.9 ]]; then + dnf install -y oracle-olcne-release-el9 + dnf config-manager --disable ol9_olcne* + dnf config-manager --enable ol9_olcne${ocne_short_version} ol9_addons ol9_baseos_latest + packageSuffix=$(getPackageSuffix) + dnf install -y olcnectl$${packageSuffix} +elif [[ ${os_version} > 7.9 && ${os_version} < 9 ]]; then + dnf install -y oracle-olcne-release-el8 + dnf config-manager --disable ol8_olcne* ol8_UEKR* + dnf config-manager --enable ol8_olcne${ocne_short_version} ol8_addons ol8_baseos_latest ${kernel_version} + packageSuffix=$(getPackageSuffix) + dnf install -y olcnectl$${packageSuffix} +else + yum install -y oracle-olcne-release-el7 + yum-config-manager --disable ol7_olcne* + yum-config-manager --enable ol7_olcne${ocne_short_version} ol7_kvm_utils ol7_addons ol7_latest + packageSuffix=$(getPackageSuffix) + yum install -y olcnectl$${packageSuffix} +fi + +# prepare SSH keys on api-server +ssh-keygen -q -N "" -f /home/${compute_user}/.ssh/id_rsa -C "${compute_user}@api-server-001" +chown ${compute_user}: /home/${compute_user}/.ssh/id_rsa* diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/locals.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/locals.tf new file mode 100644 index 00000000000..ea7a497225e --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/locals.tf @@ -0,0 +1,28 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +locals { + control_plane_nodes = contains(keys(module.control-plane-compute), "private_ip") ? (var.standalone_api_server ? flatten(module.control-plane-compute.private_ip) : flatten(concat(module.api-server-compute.private_ip, module.control-plane-compute.private_ip))) : [] + apiserver_ip = contains(keys(module.api-server-compute), "private_ip") ? element(module.api-server-compute.private_ip, 0) : "" + agents = flatten(concat(module.control-plane-compute.private_ip, module.worker-compute.private_ip)) + all_nodes = distinct(concat([local.apiserver_ip], local.control_plane_nodes, local.agents)) + total_nodes = var.control_plane_node_count + var.worker_node_count + (var.standalone_api_server ? 1 : 0) + ocne_short_version = join("", [element(split("", var.ocne_version), 0), element(split("", var.ocne_version), 2)]) + node_ocids = flatten(concat(module.control-plane-compute.node_ocids, module.worker-compute.node_ocids)) + + apiserver_init = templatefile("${path.module}/files/apiserver_init.sh", { + os_version = var.os_version + yum_repo_url = var.yum_repo_url + ocne_short_version = local.ocne_short_version + ocne_version = var.ocne_version + compute_user = var.compute_user + kernel_version = var.kernel_version + } + ) + + agent_init = templatefile("${path.module}/files/agent_init.sh", { + os_version = var.os_version + yum_repo_url = var.yum_repo_url + } + ) +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/main.tf new file mode 100644 index 00000000000..a3a8c6db6d0 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/main.tf @@ -0,0 +1,174 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +module "images" { + source = "../oci-ocne-images" + count = length(var.image_ocid) != 0 ? 0 : 1 + compartment_id = var.compartment_id + os_version = var.os_version +} + +module "kube-apiserver-loadbalancer" { + source = "../oci-ocne-loadbalancer" + compartment_id = var.compartment_id + prefix = "${var.prefix}-cp" + subnet_id = var.subnet_id + shape = var.load_balancer_shape + policy = var.load_balancer_policy + protocol = "TCP" + port = var.kube_apiserver_port + backends = { for ip in local.control_plane_nodes : ip => var.kube_apiserver_port } + backend_count = var.control_plane_node_count + instance_count = var.virtual_ip ? 0 : 1 + + # Optional + freeform_tags = var.freeform_tags +} + +module "api-server-compute" { + source = "../oci-ocne-compute" + depends_on = [module.images] + + availability_domain_id = var.availability_domain_id + compartment_id = var.compartment_id + prefix = "${var.prefix}-api-server" + init_script = local.apiserver_init + instance_count = 1 + image_ocid = length(var.image_ocid) != 0 ? var.image_ocid : module.images[0].image_ocid + subnet_id = var.subnet_id + instance_shape = var.instance_shape + ssh_public_key_path = var.ssh_public_key_path + ssh_private_key_path = var.ssh_private_key_path + enable_bastion = var.enable_bastion + bastion_public_ip = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key_path = var.bastion_private_key_path + compute_user = var.compute_user + freeform_tags = var.freeform_tags + attach_secondary_vnic = var.standalone_api_server ? false : var.virtual_ip +} + +module "control-plane-compute" { + source = "../oci-ocne-compute" + depends_on = [module.images] + + availability_domain_id = var.availability_domain_id + compartment_id = var.compartment_id + prefix = "${var.prefix}-control-plane" + init_script = local.agent_init + instance_count = var.standalone_api_server ? var.control_plane_node_count : var.control_plane_node_count - 1 + image_ocid = length(var.image_ocid) != 0 ? var.image_ocid : module.images[0].image_ocid + subnet_id = var.subnet_id + instance_shape = var.instance_shape + ssh_public_key_path = var.ssh_public_key_path + ssh_private_key_path = var.ssh_private_key_path + enable_bastion = var.enable_bastion + bastion_public_ip = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key_path = var.bastion_private_key_path + compute_user = var.compute_user + freeform_tags = var.freeform_tags + attach_secondary_vnic = var.standalone_api_server ? var.virtual_ip : false +} + +module "worker-compute" { + source = "../oci-ocne-compute" + depends_on = [module.images] + + availability_domain_id = var.availability_domain_id + compartment_id = var.compartment_id + prefix = "${var.prefix}-worker" + init_script = local.agent_init + instance_count = var.worker_node_count + image_ocid = length(var.image_ocid) != 0 ? var.image_ocid : module.images[0].image_ocid + subnet_id = var.subnet_id + instance_shape = var.instance_shape + ssh_public_key_path = var.ssh_public_key_path + ssh_private_key_path = var.ssh_private_key_path + enable_bastion = var.enable_bastion + bastion_public_ip = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key_path = var.bastion_private_key_path + compute_user = var.compute_user + freeform_tags = var.freeform_tags +} + +resource "null_resource" "copy_apiserver_ssh_key" { + triggers = { + control-plane-nodes = join(",", module.control-plane-compute.private_ip) + worker-nodes = join(",", module.worker-compute.private_ip) + } + count = length(local.agents) + depends_on = [module.api-server-compute, module.control-plane-compute, module.worker-compute] + + provisioner "local-exec" { + command = < RPM" + ] + } + + provisioner "local-exec" { + command = < /dev/null ; do :; done + + ssh -i ${var.ssh_private_key_path} -o StrictHostKeyChecking=no -o ProxyCommand="ssh -i ${var.bastion_private_key_path} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -W %h:%p ${var.bastion_user}@${var.bastion_public_ip}" ${var.compute_user}@${local.all_nodes[count.index]} \ + 'sudo dnf remove -y $RPM && \ + rm -f RPM' + fi + + if [[ ${var.enable_bastion} == false && "${var.bastion_public_ip}" == "" ]] ; then + ssh -i ${var.ssh_private_key_path} -o StrictHostKeyChecking=no ${var.compute_user}@${local.all_nodes[count.index]} \ + 'rpm -qa | grep uek > RPM && \ + sudo dnf install -y kernel-uek && \ + (sleep 2 && \ + sudo reboot)&' && \ + until ssh -i ${var.ssh_private_key_path} -o StrictHostKeyChecking=no ${var.compute_user}@${local.all_nodes[count.index]} true 2> /dev/null ; do :; done + fi + EOF + } + + provisioner "remote-exec" { + inline = [ + "sudo dnf remove -y $(cat RPM)", + "rm -f RPM" + ] + } +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/output.tf new file mode 100644 index 00000000000..845c6a7d4cd --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/output.tf @@ -0,0 +1,47 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "apiserver_ip" { + description = "IP address of API Server." + value = local.apiserver_ip +} + +output "control_plane_nodes" { + description = "List of control plane nodes." + value = local.control_plane_nodes +} + +output "worker_nodes" { + description = "List of worker node IP addresses." + value = module.worker-compute.private_ip +} + +output "load_balancer_ocid" { + description = "OCID of the load balancer." + value = module.kube-apiserver-loadbalancer.load_balancer_ocid +} + +output "load_balancer_ip" { + description = "IP address of the load balancer." + value = module.kube-apiserver-loadbalancer.ip_address +} + +output "kubernetes_endpoint" { + description = "Load balancer URI." + value = module.kube-apiserver-loadbalancer.endpoint +} + +output "node_ocids" { + description = "List of worker node IP addresses." + value = local.node_ocids +} + +output "kube_apiserver_virtual_ip" { + description = "The 2nd IP of first control plane node to be the Kubernetes API server endpoint" + value = var.virtual_ip ? (var.standalone_api_server ? module.control-plane-compute.secondary_private_ip[0] : module.api-server-compute.secondary_private_ip[0]) : "" +} + +output "image_ocid" { + description = "The OCID of the OS image to use when creating all compute resources that are part of this deployment" + value = length(var.image_ocid) != 0 ? var.image_ocid : module.images[0].image_ocid +} \ No newline at end of file diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/variables.tf new file mode 100644 index 00000000000..40ad0988dfd --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/variables.tf @@ -0,0 +1,162 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// Common OCI resource variables +variable "availability_domain_id" { + type = string + description = "The ID of the availability domain inside the `region` in which to create this deployment" +} + +variable "compartment_id" { + type = string + description = "The OCID of the compartment in which to create this deployment" +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "instance_shape" { + type = map(any) + description = "The OCI instance shape to use for all compute resources that are created as part of this deployment" +} + +variable "image_ocid" { + type = string + description = "The OCID of the OS image to use when creating all compute resources that are part of this deployment" + default = "" +} + +variable "os_version" { + type = string + description = "The version of Oracle Linux to use as the base image for all compute resources that are part of this deployemnt" +} + +variable "kernel_version" { + type = string + description = "Kernel version to be installed" +} + +variable "subnet_id" { + type = string + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this variable to set to the empty string, a network configuration will be generated automatically" +} + +// Compute instance specific variables +variable "ssh_public_key_path" { + type = string + description = "The SSH public key path to use when configuring access to any compute resources created as part of this deployment" +} + +variable "ssh_private_key_path" { + type = string + description = "THe SSH private key path that goes with the SSH public key that is used when accessing compute resources that are created as part of this deployment" +} + +// Instance pool specific variables +variable "load_balancer_shape" { + type = map(string) + description = "The OCI load balancer shape to use when creating load balancers for this deployment" +} + +variable "load_balancer_policy" { + type = string + description = "The traffic policy to apply to any load balancers that are created as part of this deployment" + default = "LEAST_CONNECTIONS" +} + +variable "ocne_version" { + type = string + description = "The version and release of OCNE to deploy" +} + +variable "yum_repo_url" { + type = string + description = "The URI of the yum repository that hosts all OCNE packages" +} + +variable "control_plane_node_count" { + type = number + description = "The number of Kubernetes control plane nodes to deploy" +} + +variable "worker_node_count" { + type = number + description = "The number of Kubernetes worker nodes to deploy" +} + +variable "standalone_api_server" { + type = bool + description = "If true, a dedicated compute instance is allocated for the OCNE API Server. Otherwise, it will be deployed onto one of the Kubernetes control plane nodes" + default = true +} + +variable "use_vault" { + type = bool + description = "Decides if Vault is used to requisition certificates for OCNE daemons. If true, then certificates are allocated using a Vault instance. Otherwise, this module will generate certificates and distribute them to each node" + default = false +} + +variable "environment_name" { + type = string + description = "The name of the OCNE Environment that is created by this module to deploy module instances into" + default = "myenvironment" +} + +variable "kubernetes_name" { + type = string + description = "The name of the instance of the OCNE Kubernetes module that is installed as part of this deployment" + default = "mycluster" +} + +variable "kube_apiserver_port" { + type = string + description = "The port to use for the Kubernetes API server that is created as part of this deployment" + default = "6443" +} + +variable "container_registry" { + type = string + description = "The container image registry that contains all container images that are consumed by this deployment" + default = "container-registry.oracle.com/olcne" +} + +variable "compute_user" { + type = string + description = "A user configured with sudo access and authenticated with ssh_public_key_path and ssh_private_key_path on each compute resource" +} + +// OCNE variables +variable "enable_bastion" { + type = bool + description = "Decides if bastion is installed. Intended for internal use. Set to false." +} + +variable "bastion_public_ip" { + type = string + description = "Public IP address of the Bastion host" +} + +variable "bastion_user" { + type = string + description = "User name on the Bastion host" + default = "opc" +} + +variable "bastion_private_key_path" { + type = string + description = "The location of the SSH private key for the Bastion host" +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} + +variable "virtual_ip" { + type = bool + description = "Setup Kubernetes API server endpoint on a virtual IP address representing all the Kubernetes control plane nodes" + default = false +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-ocne-infrastructure/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/datasources.tf new file mode 100644 index 00000000000..20b10fc291f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/datasources.tf @@ -0,0 +1,86 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +data "template_file" "vault_config_template" { + template = file("${path.module}/templates/vault.template") + + vars = { + obj_namespace = var.vault_namespace + obj_bucket = oci_objectstorage_bucket.vault_storage_backend_bucket.name + obj_lock_bucket = oci_objectstorage_bucket.vault_storage_backend_ha_bucket.name + kms_key_id = module.vault-kms.key_id + kms_crypto_endpoint = module.vault-kms.crypto_endpoint + kms_management_endpoint = module.vault-kms.management_endpoint + } +} + +data "template_file" "vault_setup" { + template = file("${path.module}/templates/vault_setup") + + vars = { + VIF = "/tmp/vault.init" + VIF_base64 = "/tmp/vault_init_base64.json" + VAULT_ADDR = "https://127.0.0.1:8200" + VAULT_SKIP_VERIFY = "true" + user_id = var.user_id + region = var.region + compartment_id = var.compartment_id + vault_ocid = var.vault_ocid + key_ocid = var.key_ocid + secret_name = var.secret_name + ocne_secret_name = var.ocne_secret_name + secretfile = "" + RT = "" + CT = "/tmp/client.token" + CT_base64 = "/tmp/client_token_base64.json" + api_key_file = "/etc/vault.d/.oci/oci_api_key.pem" + compute_user = var.compute_user + } +} + +data "template_file" "cloudinit_template" { + template = file("${path.module}/templates/cloudinit.template") + + vars = { + vault_version = var.vault_version + config = base64gzip(data.template_file.vault_config_template.rendered) + proxy = var.proxy + tenancy_id = var.tenancy_id + user_id = var.user_id + region = var.region + fingerprint = var.fingerprint + password = var.private_key_password + key_file = base64gzip(file(var.api_private_key_path)) + vault_setup = base64gzip(data.template_file.vault_setup.rendered) + lb_ip = module.vault-autoscaler.load_balancer_ip + ocne-cert-engine-policy = base64gzip(file("${path.module}/templates/ocne-cert-engine-policy.hcl")) + } +} + +data "template_cloudinit_config" "vault" { + gzip = true + base64_encode = true + + part { + filename = "vault.yaml" + content_type = "text/cloud-config" + content = data.template_file.cloudinit_template.rendered + } +} + +data "external" "fetch_token" { + depends_on = [null_resource.vault_setup] + program = ["sh", "${path.module}/templates/fetch_token"] + query = { + compute_user = var.compute_user + vault_host = module.vault-autoscaler.instances[0] + compartment_id = var.compartment_id + ocne_secret_name = var.ocne_secret_name + ssh_private_key_path = var.ssh_private_key_path + region = var.region + bastion_user = var.bastion_user + bastion_public_ip = var.bastion_public_ip + bastion_private_key_path = var.bastion_private_key_path + enable_bastion = var.enable_bastion + } +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/main.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/main.tf new file mode 100644 index 00000000000..a34b616d887 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/main.tf @@ -0,0 +1,111 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +resource "null_resource" "delete_vault_storage_backend_bucket" { + triggers = { + vault_primary_node = element(module.vault-autoscaler.instances, 0) + bastion_public_ip = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key = var.enable_bastion || var.bastion_public_ip != "" ? file(var.bastion_private_key_path) : "" + compute_user = var.compute_user + ssh_private_key_path = var.ssh_private_key_path + prefix = var.prefix + } + provisioner "remote-exec" { + when = destroy + connection { + agent = false + timeout = "10m" + host = self.triggers.vault_primary_node + user = self.triggers.compute_user + private_key = file(self.triggers.ssh_private_key_path) + bastion_host = self.triggers.bastion_public_ip + bastion_user = self.triggers.bastion_user + bastion_private_key = self.triggers.bastion_private_key + } + inline = [ + "set -x", + "export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True && oci os object bulk-delete -bn ${self.triggers.prefix}-bucket --force", + "export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True && oci os object bulk-delete -bn ${self.triggers.prefix}-ha-bucket --force" + ] + } +} + +resource "oci_objectstorage_bucket" "vault_storage_backend_bucket" { + compartment_id = var.compartment_id + name = "${var.prefix}-bucket" + namespace = var.vault_namespace + + # Optional + freeform_tags = var.freeform_tags +} + +resource "oci_objectstorage_bucket" "vault_storage_backend_ha_bucket" { + compartment_id = var.compartment_id + name = "${var.prefix}-ha-bucket" + namespace = var.vault_namespace + + # Optional + freeform_tags = var.freeform_tags +} + +module "images" { + source = "../oci-ocne-images" + + compartment_id = var.compartment_id + os_version = "8" +} + +module "vault-autoscaler" { + source = "../oci-ocne-autoscaling" + + availability_domain_id = var.availability_domain_id + compartment_id = var.compartment_id + prefix = var.prefix + instance_shape = var.instance_shape + image_ocid = module.images.image_ocid + subnet_id = var.subnet_id + ssh_public_key_path = var.ssh_public_key_path + pool_size = var.pool_size + load_balancer_protocol = var.load_balancer_protocol + load_balancer_port = var.load_balancer_port + load_balancer_ocid = var.load_balancer_ocid + load_balancer_ip = var.load_balancer_ip + load_balancer_shape = var.load_balancer_shape + create_load_balancer = var.create_load_balancer + + instance_user_data = data.template_cloudinit_config.vault.rendered + freeform_tags = var.freeform_tags +} + +module "vault-kms" { + source = "../oci-ocne-kms" + + compartment_id = var.compartment_id + prefix = var.prefix + vault_ocid = var.vault_ocid + key_ocid = var.key_ocid + freeform_tags = var.freeform_tags +} + +resource "null_resource" "vault_setup" { + depends_on = [module.vault-autoscaler] + + provisioner "remote-exec" { + connection { + agent = false + timeout = "10m" + host = element(module.vault-autoscaler.instances, 0) + user = var.compute_user + private_key = file(var.ssh_private_key_path) + bastion_host = var.bastion_public_ip + bastion_user = var.bastion_user + bastion_private_key = var.enable_bastion || var.bastion_public_ip != "" ? file(var.bastion_private_key_path) : "" + } + inline = [ + "set -x", + "sudo /usr/bin/cloud-init status --wait 1> /dev/null", + "timeout 300 bash -c 'while [[ \"$(curl -skL -o /dev/null -w ''%%{http_code}'' https://${module.vault-autoscaler.load_balancer_ip}:${var.load_balancer_port})\" != \"200\" ]]; do sleep 1; done' || false" + ] + } +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/output.tf new file mode 100644 index 00000000000..a18442b93f6 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/output.tf @@ -0,0 +1,28 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "vault_ocne_client_token" { + description = "OCNE certificate signing token." + value = data.external.fetch_token.result["vault_ocne_client_token"] +} + +output "uri" { + depends_on = [null_resource.vault_setup] + description = "Load balancer URI." + value = "https://${module.vault-autoscaler.load_balancer_ip}:${var.load_balancer_port}" +} + +output "instances" { + description = "List of vault instances." + value = module.vault-autoscaler.instances +} + +output "vault_storage_bucket" { + description = "vault storage backend bucket" + value = oci_objectstorage_bucket.vault_storage_backend_bucket.name +} + +output "vault_ha_storage_bucket" { + description = "vault storage HA backend bucket" + value = oci_objectstorage_bucket.vault_storage_backend_ha_bucket.name +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/cloudinit.template b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/cloudinit.template new file mode 100644 index 00000000000..6fb96fdb07a --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/cloudinit.template @@ -0,0 +1,128 @@ +#cloud-config +write_files: + - content: | + [Unit] + Description="HashiCorp Vault - A tool for managing secrets" + Documentation=https://www.vaultproject.io/docs/ + Requires=network-online.target + After=network-online.target + ConditionFileNotEmpty=/etc/vault.d/vault.hcl + + [Service] + User=vault + Group=vault + ProtectSystem=full + ProtectHome=read-only + PrivateTmp=yes + PrivateDevices=yes + SecureBits=keep-caps + AmbientCapabilities=CAP_IPC_LOCK + Capabilities=CAP_IPC_LOCK+ep + CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK + NoNewPrivileges=yes + ExecStart=/usr/bin/vault_env /usr/bin/vault server -config=/etc/vault.d/vault.hcl + ExecReload=/bin/kill --signal HUP $MAINPID + ExecStartPost=/usr/bin/timeout 30 sh -c 'while ! ss -H -t -l -n sport = :8200 | grep -q "^LISTEN.*:8200"; do sleep 1; done' + KillMode=process + KillSignal=SIGINT + Restart=on-failure + RestartSec=5 + TimeoutStopSec=30 + StartLimitBurst=3 + LimitNOFILE=65536 + + [Install] + WantedBy=multi-user.target + path: /usr/lib/systemd/system/vault.service + - content: | + [Unit] + Requires=vault.service + After=vault.service + + [Service] + Type=oneshot + ExecStart=/usr/bin/vault_setup + RemainAfterExit=true + + [Install] + WantedBy=multi-user.target + path: /usr/lib/systemd/system/vault-init.service + - content: | + #! /usr/bin/bash + export VAULT_API_ADDR=$(hostname):8200 + "$@" + path: /usr/bin/vault_env + permissions: 0755 + - encoding: "gzip+base64" + content: ${ocne-cert-engine-policy} + path: /etc/vault.d/ocne-cert-engine-policy.hcl + - encoding: "gzip+base64" + content: ${vault_setup} + path: /usr/bin/vault_setup + permissions: 0755 + - encoding: "gzip+base64" + content: ${key_file} + path: /root/.oci/oci_api_key.pem + - content: | + [DEFAULT] + user=${user_id} + fingerprint=${fingerprint} + key_file=/etc/vault.d/.oci/oci_api_key.pem + tenancy=${tenancy_id} + region=${region} + pass_phrase=${password} + path: /etc/vault.d/.oci/config + - encoding: "gzip+base64" + path: /etc/vault.d/.oci/oci_api_key.pem + content: ${key_file} + - encoding: "gzip+base64" + content: ${config} + path: /etc/vault.d/vault.hcl + - content: | + [req] + default_bits=2048 + encrypt_key=no + default_md=sha256 + prompt=no + utf8=yes + distinguished_name=req_distinguished_name + req_extensions=v3_req + [req_distinguished_name] + C=US + ST=California + L="Redwood City" + O="Oracle America" + OU="Private Cloud" + CN=HN + [v3_req] + subjectKeyIdentifier=hash + keyUsage=digitalSignature, keyEncipherment + extendedKeyUsage=clientAuth, serverAuth + subjectAltName=@alt_names + [ alt_names ] + DNS.1=localhost + IP.1=127.0.0.1 + IP.2=PIP + IP.3=LBIP + path: /etc/vault.d/cert.csr + permissions: 0666 + - content: | + export VAULT_SKIP_VERIFY=true + export VAULT_FORMAT=json + path: /etc/profile.d/vault.sh +runcmd: + - sed -i "s/HN/$(hostname)/" /etc/vault.d/cert.csr + - sed -i "s/LBIP/${lb_ip}/" /etc/vault.d/cert.csr + - sed -i "s/PIP/$(curl -Ls http://169.254.169.254/opc/v1/vnics | jq '.[0].privateIp')/" /etc/vault.d/cert.csr + - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/C=US/ST=California/L=Redwood City/O=Oracle America CA" -keyout /etc/vault.d/cert.key -out /etc/vault.d/cert.pem -extensions v3_req -config /etc/vault.d/cert.csr + - [sh, -c, "HTTPS_PROXY=${proxy} curl --silent --remote-name https://releases.hashicorp.com/vault/${vault_version}/vault_${vault_version}_linux_amd64.zip"] + - unzip vault_${vault_version}_linux_amd64.zip + - cp vault /usr/bin/vault + - sudo setcap cap_ipc_lock=+ep /usr/bin/vault + - sudo useradd --system --home /etc/vault.d --shell /bin/false vault + - sudo chown --recursive vault:vault /etc/vault.d + - systemctl daemon-reload + - systemctl enable vault.service + - systemctl start vault.service + - systemctl start vault-init.service + - while [ ! -f /tmp/vault_setup_finish ] ; do sleep 15 ; done diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/fetch_token b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/fetch_token new file mode 100644 index 00000000000..cc498690ece --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/fetch_token @@ -0,0 +1,34 @@ +#!/bin/bash + +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +parse_input() { + eval "$(jq -r '"compute_user=\(.compute_user) + vault_host=\(.vault_host) + compartment_id=\(.compartment_id) + ocne_secret_name=\(.ocne_secret_name) + ssh_private_key_path=\(.ssh_private_key_path) + region=\(.region) + bastion_user=\(.bastion_user) + bastion_public_ip=\(.bastion_public_ip) + bastion_private_key_path=\(.bastion_private_key_path) + enable_bastion=\(.enable_bastion)"')" +} + +fetch_token() { + if [[ ${enable_bastion} == true || "${bastion_public_ip}" != "" ]] ; then + vault_ocne_client_token=$(ssh -i ${ssh_private_key_path} -o StrictHostKeyChecking=no -o ProxyCommand="ssh -i ${bastion_private_key_path} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $bastion_user@${bastion_public_ip} nc $vault_host 22" $compute_user@$vault_host 'export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True && oci vault secret list --compartment-id '$compartment_id' --name '$ocne_secret_name' | jq -r '"'"'.data[].id'"'"' | xargs -I{} oci secrets secret-bundle get --secret-id {} --stage CURRENT --region '$region' | jq -r '"'"'.data."secret-bundle-content".content'"'"' | base64 -d' | tr -d '') + elif [[ ${enable_bastion} == false && "${bastion_public_ip}" == "" ]] ; then + vault_ocne_client_token=$(ssh -i ${ssh_private_key_path} -o StrictHostKeyChecking=no $compute_user@$vault_host 'export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True && oci vault secret list --compartment-id '$compartment_id' --name '$ocne_secret_name' | jq -r '"'"'.data[].id'"'"' | xargs -I{} oci secrets secret-bundle get --secret-id {} --stage CURRENT --region '$region' | jq -r '"'"'.data."secret-bundle-content".content'"'"' | base64 -d' | tr -d '') + fi + + if [ -z "$vault_ocne_client_token" ] ; then + echo "FATAL: problem in fetching ocne vault client token. vault_ocne_client_token=${vault_ocne_client_token}" + exit 1 + fi + jq -n --arg vault_ocne_client_token "$vault_ocne_client_token" '{"vault_ocne_client_token":$vault_ocne_client_token}' +} + +parse_input +fetch_token diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/ocne-cert-engine-policy.hcl b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/ocne-cert-engine-policy.hcl new file mode 100644 index 00000000000..2ee0985dd3f --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/ocne-cert-engine-policy.hcl @@ -0,0 +1,20 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +# Work with pki secrets engine +path "pki*" { + capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] +} + +# OCNE specific capabilities +path "+/ocne*" { + capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] +} + +path "+/+/ocne*" { + capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] +} + +path "+/+/+/ocne*" { + capabilities = [ "create", "read", "update", "delete", "list", "sudo" ] +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault.template b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault.template new file mode 100644 index 00000000000..31a0ed6520c --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault.template @@ -0,0 +1,23 @@ +ui = true +storage "oci" { + namespace_name = "${obj_namespace}" + bucket_name = "${obj_bucket}" + ha_enabled = "true" + lock_bucket_name = "${obj_lock_bucket}" + auth_type_api_key = "true" +} +seal "ocikms" { + key_id = "${kms_key_id}" + crypto_endpoint = "${kms_crypto_endpoint}" + management_endpoint = "${kms_management_endpoint}" + auth_type_api_key = "true" +} +listener "tcp" { + address = "[::]:8200" + cluster_address = "[::]:8201" + tls_cipher_suites = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA" + tls_disable = false + tls_prefer_server_cipher_suites = true + tls_cert_file = "/etc/vault.d/cert.pem" + tls_key_file = "/etc/vault.d/cert.key" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_env b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_env new file mode 100644 index 00000000000..f2c03a60104 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_env @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +#! /usr/bin/bash + +export VAULT_API_ADDR=$(hostname):8200 + +"$@" diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_setup b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_setup new file mode 100644 index 00000000000..7e659a63f8a --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/templates/vault_setup @@ -0,0 +1,98 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +#! /usr/bin/bash + +firewall-cmd --add-port=8200/tcp +firewall-cmd --add-port=8200/tcp --permanent +firewall-cmd --add-port=8201/tcp +firewall-cmd --add-port=8201/tcp --permanent + +export VIF=${VIF} +export VIF_base64=${VIF_base64} +export VAULT_ADDR=${VAULT_ADDR} +export VAULT_SKIP_VERIFY=${VAULT_SKIP_VERIFY} +export VAULT_FORMAT=json +export OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True +export CT=${CT} +export CT_base64=${CT_base64} + + +vault operator init -status +if [ $? -ne 2 ]; then + exit 0 +fi + + +## OCI Client configuration +dnf config-manager --set-enabled ol8_developer +yum install -y python36-oci-cli +cp /etc/vault.d/.oci/config ~/.oci/ +oci iam user api-key upload --user-id ${user_id} --key-file ${api_key_file} +mkdir /home/${compute_user}/.oci/ +cp /etc/vault.d/.oci/config /home/${compute_user}/.oci/ +chown -R ${compute_user}: /home/${compute_user}/.oci + + +## Vault configuration +vault operator init -format=json > ${VIF} +base64 ${VIF} > ${VIF_base64} +secretfile="$(< ${VIF_base64})" + +if [[ ! `oci vault secret list --compartment-id ${compartment_id} | grep -w ${secret_name}` ]] + then + oci vault secret create-base64 --compartment-id ${compartment_id} --secret-name ${secret_name} --vault-id ${vault_ocid} --key-id ${key_ocid} --secret-content-content "$secretfile" --secret-content-name "vault_recovery_keys" --region ${region} + else + secret_id=$(oci vault secret list --compartment-id ${compartment_id} --name ${secret_name} | grep vaultsecret | awk -F '"' '{print $4}') + oci vault secret update-base64 --secret-id $secret_id --secret-content-content "$secretfile" +fi + +RT=$(oci vault secret list --compartment-id ${compartment_id} --name ${secret_name} | jq -r '.data[].id' | xargs -I{} oci secrets secret-bundle get --secret-id {} --stage CURRENT --region ${region} | jq -r '.data."secret-bundle-content".content' | base64 -d | jq -r '.root_token') + + +for i in {1..10}; do + S=$(vault status) + if [ $? -eq 0 ]; then + echo "$S" | grep -q 'HA Mode.*active' + if [ $? -eq 0 ]; then + break + fi + fi + sleep 5 +done + +vault login "$RT" +vault secrets enable pki +vault secrets tune -max-lease-ttl=87600h pki +vault write -field=certificate pki/root/generate/internal common_name="ocne" ttl=87600h +vault write pki/config/urls issuing_certificates="${VAULT_ADDR}/v1/pki/ca" crl_distribution_points="${VAULT_ADDR}/v1/pki/crl" +vault secrets enable -path=ocne_pki_intermediary pki +vault secrets tune -max-lease-ttl=87600h ocne_pki_intermediary +vault write -format=json ocne_pki_intermediary/intermediate/generate/internal common_name="ocne Intermediate Authority" ttl="87600h" | jq -r '.data.csr' > pki_intermediate.csr +vault write -format=json pki/root/sign-intermediate csr=@pki_intermediate.csr format=pem_bundle ttl="87600h" | jq -r '.data.certificate' > intermediate.cert.pem +vault write ocne_pki_intermediary/intermediate/set-signed certificate=@intermediate.cert.pem +vault write ocne_pki_intermediary/roles/ocne allow_any_name=true use_csr_common_name=false require_cn=false use_csr_san=false max_ttl="87600h" + + +## Create vault token based on the OCNE cert policy. +vault policy write ocne-cert-engine-policy /etc/vault.d/ocne-cert-engine-policy.hcl +vault token create -policy=ocne-cert-engine-policy | grep -w client_token | awk -F '"' '{print $4}' > ${CT} + +base64 ${CT} > ${CT_base64} +secretfile="$(< ${CT_base64})" + +if [[ ! `oci vault secret list --compartment-id ${compartment_id} | grep -w ${ocne_secret_name}` ]] + then + oci vault secret create-base64 --compartment-id ${compartment_id} --secret-name ${ocne_secret_name} --vault-id ${vault_ocid} --key-id ${key_ocid} --secret-content-content "$secretfile" --secret-content-name "vault_recovery_keys" --region ${region} + else + secret_id=$(oci vault secret list --compartment-id ${compartment_id} --name ${ocne_secret_name} | grep vaultsecret | awk -F '"' '{print $4}') + oci vault secret update-base64 --secret-id $secret_id --secret-content-content "$secretfile" +fi + + +# Remove files, which could lead to security breaches. +rm -f pki_intermediate.csr intermediate.cert.pem ${VIF} ${VIF_base64} ${CT} ${CT_base64} + + +# Touch finish file that can be checked by cloud-init. +touch /tmp/vault_setup_finish diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/variables.tf new file mode 100644 index 00000000000..ad0eb7df557 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/variables.tf @@ -0,0 +1,181 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// OCI Identity Variables +variable "user_id" { + type = string + description = "The OCID of the user that will be used by Vault to access OCI resources" +} + +variable "region" { + type = string + description = "The ID of the OCI Region where Vault is deployed" +} + +variable "tenancy_id" { + type = string + description = "The OCID of the OCI Tenancy where Vault is deployed" +} + +variable "fingerprint" { + type = string + description = "The fingerprint associated with the private key used by the OCI user to authentication" +} + +variable "api_private_key_path" { + type = string + description = "The path to the private key used by the OCI user to authenticate" +} + +variable "private_key_password" { + type = string + description = "The password used to decrypt the OCI user's private key" +} + +// Common OCI resource variables +variable "availability_domain_id" { + type = string + description = "The ID of the availability domain inside the `region` in which to create this deployment" +} + +variable "compartment_id" { + type = string + description = "The OCID of the compartment in which to create this deployment" +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "instance_shape" { + type = map(any) + description = "The OCI instance shape to use for all compute resources that are created as part of this deployment" +} + +variable "subnet_id" { + type = string + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this variable to set to the empty string, a network configuration will be generated automatically" +} + +variable "compute_user" { + type = string + description = "A user configured with sudo access and authenticated with ssh_public_key and ssh_private_key on each compute resource" +} + +// Compute instance specific variables +variable "ssh_public_key_path" { + type = string + description = "The SSH public key path to use when configuring access to any compute resources created as part of this deployment" +} + +variable "ssh_private_key_path" { + type = string + description = "THe SSH private key path that goes with the SSH public key that is used when accessing compute resources that are created as part of this deployment" +} + +// Instance pool specific variables +variable "pool_size" { + type = number + description = "The initial number of Vault instances to spawn as part of this deployment" + default = "1" +} + + +variable "load_balancer_protocol" { + type = string + description = "The protocol to select when creating the listener for the load balancer that talks to the Vault instances" + default = "TCP" +} + +variable "load_balancer_ocid" { + type = string + description = "The OCID of the load balancer to use. If one is not specified, a load balancer will be created" + default = "" +} + +variable "create_load_balancer" { + type = string + description = "Set to true if a load balancer should be created by this module to service Vault instances. Set to false to use an existing one" + default = true +} + +variable "load_balancer_ip" { + type = string + description = "If a custom load balancer is provided, set this to the canonical IP address of that load balancer" + default = "" +} + +variable "load_balancer_port" { + type = string + description = "The port that the generated load balancer listener is listening on" + default = "8200" +} + +variable "vault_namespace" { + type = string + description = "OCI Object storage bucket namespace string used to create the OCI Object Storage Bucket to support the [HashiCorp Vault OCI Object Storage Backend](https://www.vaultproject.io/docs/configuration/storage/oci-object-storage). To get the namespace string see [Understanding Object Storage Namespaces](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/understandingnamespaces.htm]" +} + +variable "load_balancer_shape" { + type = map(string) + description = "The OCI shape of the load balancer to create to service this pool" +} + +variable "vault_version" { + type = string + description = "The version of Vault that is installed" + default = "1.3.4" +} + +variable "proxy" { + type = string + description = "The https proxy that is used to download Vault. Set to the empty string if no proxy is required" +} + +variable "vault_ocid" { + type = string + description = "The OCID of the OCI KMS Vault that stores the OCI KSM Vault Key that secures the auto-unseal data for Hashicorp Vault. If unset, an OCI KMS Vault will be created" +} + +variable "key_ocid" { + type = string + description = "The OCID of an OCI KMS Vault Key that is within the OCI KMS Vault configured via `vault_ocid`. If not is not provided, an OCI KMS Vault key will be created" +} + +variable "secret_name" { + type = string + description = "The name of the vault secret" +} + +variable "ocne_secret_name" { + type = string + description = "The name of the ocne vault secret" +} + +variable "enable_bastion" { + type = bool + description = "Decides if bastion is installed. Intended for internal use. Set to false." +} + +variable "bastion_public_ip" { + type = string + description = "Public IP address of an existing Bastion host. This is set when we are not creating a bastion but need to use an existing one." +} + +variable "bastion_user" { + type = string + description = "User name on the Bastion host" + default = "opc" +} + +variable "bastion_private_key_path" { + type = string + description = "The SSH private key path that goes with the SSH public key that is used when accessing the bastion host. Must be set to true if enable_bastion is set to true." +} + +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/versions.tf new file mode 100644 index 00000000000..204a2eea628 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/terraform-oci-vault/versions.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/output.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/output.tf new file mode 100644 index 00000000000..7e58757c0b3 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/output.tf @@ -0,0 +1,270 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +output "apiserver_ip" { + description = "IP address of operator node." + value = module.infrastructure.apiserver_ip +} + +output "control_plane_nodes" { + description = "IP addresses of control plane nodes." + value = module.infrastructure.control_plane_nodes +} + +output "worker_nodes" { + description = "IP addresses of worker nodes." + value = module.infrastructure.worker_nodes +} + +output "vault_instances" { + description = "IP addresses of vault instances." + value = var.use_vault ? module.vault[0].instances : [] +} + +output "vault_uri" { + description = "OCI vault URL." + value = var.use_vault ? module.vault[0].uri : "" +} + +output "ocne_vault_client_token" { + description = "OCNE certificate signing token." + value = var.use_vault ? module.vault[0].vault_ocne_client_token : "" + # sensitive = true +} + +output "vault_storage_bucket" { + description = "vault storage backend bucket" + value = var.use_vault ? module.vault[0].vault_storage_bucket : "" +} + +output "vault_ha_storage_bucket" { + description = "vault storage HA backend bucket" + value = var.use_vault ? module.vault[0].vault_ha_storage_bucket : "" +} + +output "user_id" { + description = "The OCID of the user that will be used by Vault to access OCI resources. This value cannot be empty if Vault is being deployed." + value = var.user_id +} + +output "region" { + description = "The ID of the OCI Region where Vault is deployed. This value cannot be empty if Vault is being deployed." + value = var.region +} + +output "tenancy_id" { + description = "The OCID of the OCI Tenancy where Vault is deployed. This value cannot be empty if Vault is being deployed." + value = var.tenancy_id +} + +output "fingerprint" { + description = "The fingerprint associated with the private key used by the OCI user to authentication. This value cannot be empty if Vault is being deployed." + value = var.fingerprint +} + +output "api_private_key_path" { + description = "The path to the private key used by the OCI user to authenticate. This value cannot be empty if Vault is being deployed." + value = var.api_private_key_path +} + +// Common OCI resource outputs +output "availability_domain_id" { + description = "The ID of the availability domain inside the `region` in which to create this deployment" + value = var.availability_domain_id +} + +output "compartment_id" { + description = "The OCID of the compartment in which to create this deployment" + value = var.compartment_id +} + +output "prefix" { + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" + value = var.prefix +} + +output "instance_shape" { + description = "The OCI instance shape to use for all compute resources that are created as part of this deployment" + value = var.instance_shape +} + +output "image_ocid" { + description = "The OCID of the OS image to use when creating all compute resources that are part of this deployment" + value = module.infrastructure.image_ocid +} + +output "os_version" { + description = "The version of Oracle Linux to use as the base image for all compute resources that are part of this deployemnt" + value = var.os_version +} + +output "subnet_id" { + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this output to set to the empty string, a network configuration will be generated automatically" + value = var.deploy_networking ? module.oci-ocne-network[0].private_subnet_id.*.id[0] : var.subnet_id +} + +// Compute instance specific outputs +output "ssh_private_key_path" { + description = "THe SSH private key path that goes with the SSH public key that is used when accessing compute resources that are created as part of this deployment" + value = var.ssh_private_key_path +} + +// Instance pool specific outputs +output "vault_pool_size" { + description = "The initial number of Vault instances to spawn as part of this deployment" + value = var.vault_pool_size +} + +output "load_balancer_ip" { + description = "The IP of theOCI load balancer indiciating where Kubernetes clients should connect to when communicating with the Kubernetes cluster" + value = module.infrastructure.load_balancer_ip +} + +output "load_balancer_shape" { + description = "The OCI load balancer shape to use when creating load balancers for this deployment" + value = var.load_balancer_shape +} + +output "load_balancer_policy" { + description = "The traffic policy to apply to any load balancers that are created as part of this deployment" + value = var.load_balancer_policy +} + +output "vault_namespace" { + description = "The OCI object storage namespace under which object storage buckets for Vault are created as part of this deployment" + value = var.vault_namespace +} + +output "vault_version" { + description = "The version of Vault to deploy" + value = var.vault_version +} + +output "proxy" { + description = "A proxy server to use for https and http communication when downloading or otherwise fetching data from external networks" + value = var.proxy +} + +output "ocne_version" { + description = "The version and release of OCNE to deploy" + value = data.external.ocne_config.result.version +} + +output "yum_repo_url" { + description = "The URI of the yum repository that hosts all OCNE packages" + value = var.yum_repo_url +} + +output "control_plane_node_count" { + description = "The number of Kubernetes control plane nodes to deploy" + value = var.control_plane_node_count +} + +output "worker_node_count" { + description = "The number of Kubernetes worker nodes to deploy" + value = var.worker_node_count +} + +output "standalone_api_server" { + description = "If true, a dedicated compute instance is allocated for the OCNE API Server. Otherwise, it will be deployed onto one of the Kubernetes control plane nodes" + value = var.standalone_api_server +} + +output "environment_name" { + description = "The name of the OCNE Environment that is created by this module to deploy module instances into" + value = var.environment_name +} + +output "kubernetes_name" { + description = "The name of the instance of the OCNE Kubernetes module that is installed as part of this deployment" + value = var.kubernetes_name +} + +output "kube_apiserver_port" { + description = "The port to use for the Kubernetes API server that is created as part of this deployment" + value = var.kube_apiserver_port +} + +output "container_registry" { + description = "The container image registry that contains all container images that are consumed by this deployment" + value = var.container_registry +} + +output "extra_cas" { + description = "Any extra trusted certificates for compute resources" + value = var.extra_cas +} + +// Networking outputs +output "vcn_id" { + description = "The OCID of the OCI Virtual Cloud Network in which to create any subnets that might be generated as part of this deployment" + value = var.deploy_networking ? module.oci-ocne-network[0].vcn_id : var.vcn_id +} + +// Vault outputs +output "use_vault" { + description = "Decides if Vault is used to requisition certificates for OCNE daemons. If true, then certificates are allocated using a Vault instance. Otherwise, this module will generate certificates and distribute them to each node" + value = var.use_vault +} + +output "vault_ocid" { + description = "The OCID of the OCI KMS Vault to use with the Hashicorp Vault automatic unsealing feature" + value = var.vault_ocid +} + +output "key_ocid" { + description = "The OCID of the OCI KMS Vault Key to use with the Hashicorp Vault automatic unsealing feature" + value = var.key_ocid +} + +output "secret_name" { + description = "The name of the vault secret" + value = local.secret_name +} + +output "ocne_secret_name" { + description = "The name of the ocne vault secret" + value = local.ocne_secret_name +} + +output "bastion_private_key_path" { + value = var.bastion_private_key_path +} + +output "bastion_public_ip" { + value = var.enable_bastion ? module.bastion[0].bastion_public_ip : var.bastion_public_ip +} + +output "bastion_user" { + value = var.bastion_user +} + +output "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + value = var.freeform_tags +} + +output "provision_mode" { + description = "Specifies the provision mode." + value = var.provision_mode +} + +output "provision_modes_map" { + description = "Map that contains allowed provision modes." + value = local.provision_modes_map +} + +output "kube_apiserver_virtual_ip" { + description = "The virtual IP address to be Kubernetes API server endpoint" + value = var.virtual_ip ? module.infrastructure.kube_apiserver_virtual_ip : "" +} + +output "kube_apiserver_endpoint_ip" { + description = "The Kubernetes API server endpoint" + value = var.virtual_ip ? module.infrastructure.kube_apiserver_virtual_ip : module.infrastructure.load_balancer_ip +} + +output "config_file_path" { + description = "The path to the OCNE configuration file" + value = var.config_file_path +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/provider.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/provider.tf new file mode 100644 index 00000000000..d7c6d1218a2 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/provider.tf @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +provider "oci" { + disable_auto_retries = false + fingerprint = var.fingerprint + private_key_path = var.api_private_key_path + region = var.region + retry_duration_seconds = 60 + tenancy_ocid = var.tenancy_id + user_ocid = var.user_id +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/terraform.tfvars.template b/integration-tests/src/test/resources/ocne/terraform/1.9/terraform.tfvars.template new file mode 100644 index 00000000000..bfea1ed84cc --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/terraform.tfvars.template @@ -0,0 +1,41 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// OCI Identity Variables +tenancy_id = "@OCI_TENANCY_ID@" +compartment_id = "@OCI_COMPARTMENT_ID@" +user_id = "@OCI_USER_ID@" +fingerprint = "@OCI_FINGERPRINT@" +api_private_key_path = "@OCI_API_PRIVATE_KEY_PATH@" + +// Common OCI resource variables +region = "@OCI_REGION@" +availability_domain_id = "@OCI_AVAILABILITY_DOMAIN_ID@" +prefix = "@OCI_INSTANCE_PREFIX@" + +// network variables +deploy_networking = "@OCI_DEPLOY_NETWORKING@" +subnet_id = "@OCI_SUBNET_ID@" +vcn_id = "@OCI_VCN_ID@" + +// Compute instance specific variables +ssh_public_key_path = "@OCI_SSH_PUBLIC_KEY_PATH@" +ssh_private_key_path = "@OCI_SSH_PRIVATE_KEY_PATH@" + +// Bastion specific variables +enable_bastion = "@OCI_ENABLE_BASTION@" + +virtual_ip = "@OCI_VIRTUAL_IP@" + +// OCNE Overrides +control_plane_node_count = "@OCNE_CONTROL_PLANE_NODE_COUNT@" +worker_node_count = "@OCNE_WORKER_NODE_COUNT@" +environment_name = "@OCNE_ENVIRONMENT_NAME@" +kubernetes_name = "@OCNE_K8S_CLUSTER_NAME@" + +// OCNE version +ocne_version = "@OCNE_VERSION@" + +// proxy +proxy = "@HTTP_PROXY@" +no_proxy = "@NO_PROXY@" diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/variables.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/variables.tf new file mode 100644 index 00000000000..8aa67b49afe --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/variables.tf @@ -0,0 +1,344 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +// OCI Identity Variables +variable "user_id" { + type = string + description = "The OCID of the user that will be used by terraform to create OCI resources. To get the value, see Where to Get the Tenancy's OCID and User's OCID." +} + +variable "region" { + type = string + description = "The OCI region where resources will be created. To get the value, See Regions and Availability Domains - https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm#top" + default = "" +} + +variable "tenancy_id" { + type = string + description = "The OCID of your tenancy. To get the value, see Where to Get the Tenancy's OCID and User's OCID - https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#five." +} + +variable "api_private_key_path" { + type = string + description = "The path to the private key used by the OCI user to authenticate with OCI API's. OCI plugin for the HashiCorp Vault allows only the 4096 key size for the OCI API signing key (RSA key pair in PEM format). So please use the 4096 key size when creating the RSA key pair. For details on how to create and configure keys see How to Generate an API Signing Key (https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#two) and How to Upload the Public Key (https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#three)" + default = "" +} + +variable "private_key_password" { + type = string + description = "The password used to decrypt the OCI user's private key. (Optional) Passphrase used for the api_private_key, if it is encrypted." + default = "" +} + +variable "fingerprint" { + type = string + description = "Fingerprint for the key pair being used. To get the value, see How to Get the Key's Fingerprint. https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#four" +} + + +// Common OCI resource variables +variable "availability_domain_id" { + type = string + description = "The ID of the availability domain inside the `region` in which to create this deployment" +} + +variable "compartment_id" { + type = string + description = "The OCID of the compartment." +} + +variable "prefix" { + type = string + description = "A unique prefix to attach to the name of all cloud resources that are created as a part of this deployment" +} + +variable "instance_shape" { + type = map(any) + description = "The OCI instance shape to use for all compute resources that are created as part of this deployment" + default = { + shape = "VM.Standard.E4.Flex", ocpus = 2, memory = 64 + } +} + +variable "image_ocid" { + type = string + description = "The OCID of the OS image to use when creating all compute resources that are part of this deployment" + default = "" +} + +variable "os_version" { + type = string + description = "The version of Oracle Linux to use as the base image for all compute resources that are part of this deployemnt" + default = "8" +} + +variable "kernel_version" { + type = string + description = "Kernel version to be installed" + default = "ol8_UEKR6" +} + +variable "compute_user" { + type = string + description = "A user configured with sudo access and authenticated with ssh_public_key and ssh_private_key on each compute resource" + default = "opc" +} + +variable "node_ocids" { + description = "Comma separated list of Kubernetes nodes (both control plane and worker nodes) with their Oracle Cloud Identifiers (OCIDs). The format for the list is: FQDN=OCID,..." + default = "" +} + +// Compute instance specific variables +variable "ssh_public_key_path" { + type = string + description = "The SSH public key path to use when configuring access to any compute resources created as part of this deployment" +} + +variable "ssh_private_key_path" { + type = string + description = "The SSH private key path that goes with the SSH public key that is used when accessing compute resources that are created as part of this deployment" +} + +// Instance pool specific variables +variable "vault_pool_size" { + type = number + description = "The initial number of Vault instances to spawn as part of this deployment" + default = "1" +} + +variable "load_balancer_shape" { + type = map(string) + description = "The OCI load balancer shape to use when creating load balancers for this deployment" + default = { + shape = "flexible" + flex_min = "10" + flex_max = "50" + } +} + +variable "load_balancer_policy" { + type = string + description = "The traffic policy to apply to any load balancers that are created as part of this deployment" + default = "LEAST_CONNECTIONS" +} + +variable "vault_namespace" { + type = string + description = "OCI Object storage bucket namespace string used to create the OCI Object Storage Bucket to support the [HashiCorp Vault OCI Object Storage Backend](https://www.vaultproject.io/docs/configuration/storage/oci-object-storage). To get the namespace string see [Understanding Object Storage Namespaces](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/understandingnamespaces.htm]." + default = "" +} + +variable "vault_version" { + type = string + description = "The version of Vault to deploy" + default = "1.3.4" +} + +variable "proxy" { + type = string + description = "A proxy server to use for https and http communication when downloading or otherwise fetching data from external networks" + default = "" +} + +variable "no_proxy" { + type = string + description = "No proxy setting, if used" + default = "" +} + +variable "ocne_version" { + type = string + description = "The version and release of OCNE to deploy. For more details on the versions, please see the [OCNE Release Notes](https://docs.oracle.com/en/operating-systems/olcne/1.7/relnotes/components.html#components). The default value '1.7' is to install the latest patch version of 1.7. To install a specific patch version, please set the value to `` or `-`." + default = "1.7" +} + +variable "yum_repo_url" { + type = string + description = "The URI of the yum repository that hosts all OCNE packages" + default = "http://yum.oracle.com/repo/OracleLinux/OL8/olcne16/x86_64" +} + +variable "control_plane_node_count" { + type = number + description = "The number of Kubernetes control plane nodes to deploy" + default = 3 +} + +variable "worker_node_count" { + type = number + description = "The number of Kubernetes worker nodes to deploy" + default = 3 +} + +variable "standalone_api_server" { + type = bool + description = "If true, a dedicated compute instance is allocated for the OCNE API Server. Otherwise, it will be deployed onto one of the Kubernetes control plane nodes" + default = true +} + +variable "environment_name" { + type = string + description = "The name of the OCNE Environment that is created by this module to deploy module instances into" + default = "myenvironment" +} + +variable "kubernetes_name" { + type = string + description = "The name of the instance of the OCNE Kubernetes module that is installed as part of this deployment" + default = "mycluster" +} + +variable "kube_apiserver_port" { + type = string + description = "The port to use for the Kubernetes API server that is created as part of this deployment" + default = "6443" +} + +variable "container_registry" { + type = string + description = "The container image registry that contains all container images that are consumed by this deployment" + default = "container-registry.oracle.com/olcne" +} + +variable "extra_cas" { + type = list(any) + description = "Any extra trusted certificates for compute resources" + default = [] +} + +// Networking variables +variable "vcn_id" { + type = string + description = "The OCID of the OCI Virtual Cloud Network in which to create any subnets that might be generated as part of this deployment" + default = "" +} + +variable "subnet_id" { + type = string + description = "The OCID of a pre-existing subnet that all newly created cloud resources will be configured to use. If this variable to set to the empty string, a network configuration will be generated automatically" + default = "" +} + +variable "ig_route_id" { + type = string + default = "" +} + +variable "nat_route_id" { + type = string + default = "" +} + +variable "deploy_networking" { + type = bool + description = "Decides if VCN is installed." + default = true +} + +// Vault variables +variable "use_vault" { + type = bool + description = "Decides if Vault is used to requisition certificates for OCNE daemons. If true, then certificates are allocated using a Vault instance. Otherwise, this module will generate certificates and distribute them to each node" + default = false +} + +variable "vault_ocid" { + type = string + description = "The OCID of the OCI KMS Vault to use with the Hashicorp Vault automatic unsealing feature" + default = "" +} + +variable "key_ocid" { + type = string + description = "The OCID of the OCI KMS Vault Key to use with the Hashicorp Vault automatic unsealing feature" + default = "" +} + +variable "secret_name" { + type = string + description = "The name of the vault secret" + default = "vault_keys" +} + +variable "ocne_secret_name" { + type = string + description = "The name of the ocne vault secret" + default = "ocne_keys" +} + +variable "enable_bastion" { + type = bool + description = "Decides if bastion is installed. Intended for internal use. Set to false." + default = true +} + +variable "bastion_shape" { + type = map(any) + description = "The shape of bastion instance." + default = { + shape = "VM.Standard.E3.Flex", ocpus = 1, memory = 4 + } +} + +variable "bastion_public_ip" { + type = string + description = "Public IP address of an existing Bastion host. This is set when we are not creating a bastion but need to use an existing one." + default = "" +} + +variable "bastion_user" { + type = string + description = "User name on the Bastion host" + default = "opc" +} + +variable "bastion_private_key_path" { + type = string + description = "The SSH private key path that goes with the SSH public key that is used when accessing the bastion host. Must be set if enable_bastion is set to true." + default = "" +} + +variable "enable_notification" { + description = "Whether to enable ONS notification for the bastion host." + default = false + type = bool +} + +# The three tags, department, environment, and role, were moved +# from modules/terraform-oci-bastion/variables.tf +variable "freeform_tags" { + description = "Freeform tags with useful miscellaneous information." + type = map(any) + default = {} +} + +variable "restrict_service_externalip_cidrs" { + type = string + description = "A set of CIDR blocks to allow for the externalIp field in Kubernetes Services" + default = "" +} + +variable "debug" { + type = bool + description = "Enable provision debug logging" + default = false +} + +variable "provision_mode" { + type = string + description = "Specifies the provision mode." + default = "OCNE" +} + +variable "virtual_ip" { + type = bool + description = "Setup Kubernetes API server endpoint on a virtual IP address representing all the Kubernetes control plane nodes" + default = false +} + +variable "config_file_path" { + type = string + description = "The path to the OCNE configuration file - https://docs.oracle.com/en/operating-systems/olcne/1.7/olcnectl/config.html" + default = "" +} diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/versions.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/versions.tf new file mode 100644 index 00000000000..2dfad687296 --- /dev/null +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/versions.tf @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl + +terraform { + required_providers { + oci = { + source = "oracle/oci" + version = "6.15.0" + #version = ">= 4.115.0" + } + } + required_version = ">= 1.0.0" +} diff --git a/validateScripts.sh b/validateScripts.sh index 4adcc588763..1c7894b26cf 100755 --- a/validateScripts.sh +++ b/validateScripts.sh @@ -23,9 +23,10 @@ validate_script() { } -EXCLUDE_PATH="*/docker-images/*" +EXCLUDE_PATH1="*/docker-images/*" +EXCLUDE_PATH2="*/ocne/terraform/1.9/*" -find "$(pwd -P)" -type f -name '*.sh' -not -path "$EXCLUDE_PATH" -print0 | { +find "$(pwd -P)" -type f -name '*.sh' -not -path "$EXCLUDE_PATH1" -not -path "$EXCLUDE_PATH2" -print0 | { return_code=0 From b24b660237a654058c11ca3c61699ce4838882a7 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Tue, 12 Nov 2024 22:14:08 +0000 Subject: [PATCH 221/356] Fix the case that when a pod is not ready because of readiness probe permanent... --- .../java/oracle/kubernetes/operator/PodWatcher.java | 3 ++- .../kubernetes/operator/helpers/PodHelper.java | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/PodWatcher.java b/operator/src/main/java/oracle/kubernetes/operator/PodWatcher.java index 22c4ee354e0..3b9d5c5108d 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/PodWatcher.java +++ b/operator/src/main/java/oracle/kubernetes/operator/PodWatcher.java @@ -277,7 +277,8 @@ private WaitForPodReadyStep(String podName, Step next) { // A pod is ready if it is not being deleted and has the ready status. @Override protected boolean isReady(V1Pod result) { - return result != null && !PodHelper.isDeleting(result) && PodHelper.isReady(result); + return result != null && (!PodHelper.isDeleting(result) && (PodHelper.isReady(result) + || PodHelper.isWaitingToRoll(result))); } // Pods should be processed if ready. diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java index 5f16b93a943..327d32b4ca5 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java @@ -115,6 +115,19 @@ public static boolean isReady(V1Pod pod) { return ready; } + /** + * Is the pod waiting to roll. + * @param pod pod + * @return true if the pod is waiting to roll + */ + public static boolean isWaitingToRoll(V1Pod pod) { + return Optional.ofNullable(pod) + .map(V1Pod::getMetadata) + .map(V1ObjectMeta::getLabels) + .map(labels -> "true".equalsIgnoreCase(labels.get(LabelConstants.TO_BE_ROLLED_LABEL))) + .orElse(false); + } + static boolean hasReadyServer(V1Pod pod) { return Optional.ofNullable(pod).map(PodHelper::hasReadyStatus).orElse(false); } From 313c89810db3f0937c08f5965d1688a5651a3dff Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 13 Nov 2024 14:42:14 +0000 Subject: [PATCH 222/356] Added more checks to fix intermittent failures in Load Balancers tests, added LB tests to weekly --- Jenkinsfile.oke | 2 +- Jenkinsfile.podman | 4 +-- .../kubernetes/ItCrossDomainTransaction.java | 15 +++++++++- ...tHorizontalPodAutoscalerCustomMetrics.java | 2 +- .../kubernetes/ItLBTwoDomainsNginx.java | 7 +++++ .../kubernetes/ItLBTwoDomainsTraefik.java | 12 +++++++- .../ItMonitoringExporterSideCar.java | 2 +- .../weblogic/kubernetes/ItRemoteConsole.java | 17 ++++++++++- .../kubernetes/utils/CommonLBTestUtils.java | 4 +-- .../kubernetes/utils/LoadBalancerUtils.java | 30 ++++++++++++++++--- .../kubernetes/utils/MonitoringUtils.java | 14 +++++---- 11 files changed, 90 insertions(+), 19 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 96617571bcc..b3398caa805 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -153,7 +153,7 @@ pipeline { ) string(name: 'BRANCH', description: '', - defaultValue: "main" + defaultValue: "release/4.2" ) choice(name: 'BASE_IMAGES_REPO', choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 2e661a3ca39..fcf3f71d74d 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -18,8 +18,8 @@ def kind_k8s_map = [ ] ] def _kind_image = null -CRON_SETTINGS = '''H 1 * * 0-4 % MAVEN_PROFILE_NAME=kind-parallel - H 2 * * 0-4 % MAVEN_PROFILE_NAME=kind-sequential''' +CRON_SETTINGS = '''H 1 * * 1-5 % MAVEN_PROFILE_NAME=kind-parallel + H 2 * * 1-5 % MAVEN_PROFILE_NAME=kind-sequential''' pipeline { agent { label 'large-ol9u4' } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java index 3b484bd249d..27f8adc3932 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItCrossDomainTransaction.java @@ -46,6 +46,7 @@ import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; import oracle.weblogic.kubernetes.utils.OracleHttpClient; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -71,6 +72,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.createDomainCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; +import static oracle.weblogic.kubernetes.actions.TestActions.uninstallNginx; import static oracle.weblogic.kubernetes.assertions.TestAssertions.domainExists; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReady; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.checkAppIsActive; @@ -112,7 +114,7 @@ */ @DisplayName("Verify cross domain transaction is successful") @IntegrationTest -@Tag("oke-sequential") +@Tag("oke-weekly-sequential") @Tag("kind-parallel") @Tag("okd-wls-srg") class ItCrossDomainTransaction { @@ -229,6 +231,17 @@ public void beforeEach() { } } + @AfterAll + public void tearDownAll() { + + if (nginxHelmParams != null && OKE_CLUSTER) { + assertThat(uninstallNginx(nginxHelmParams.getHelmParams())) + .as("Test uninstallNginx returns true") + .withFailMessage("uninstallNginx() did not return true") + .isTrue(); + } + } + private static void updatePropertyFile() { //create a temporary directory to copy and update the properties file Path target = Paths.get(PROPS_TEMP_DIR); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java index a370aa4c5d3..d4e7e9db435 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItHorizontalPodAutoscalerCustomMetrics.java @@ -113,7 +113,7 @@ @DisplayName("Test to a create MII domain and test autoscaling using HPA and" + "custom metrics provided via use of monitoring exporter and prometheus and prometheus adapter") @IntegrationTest -@Tag("oke-sequential") +@Tag("oke-weekly-sequential") @Tag("kind-parallel") public class ItHorizontalPodAutoscalerCustomMetrics { private static final String MONEXP_MODEL_FILE = "model.monexp.custommetrics.yaml"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index 1d174860f0e..1f8806e3d09 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -50,6 +50,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; +import static oracle.weblogic.kubernetes.actions.TestActions.uninstallNginx; import static oracle.weblogic.kubernetes.utils.CommonLBTestUtils.buildAndDeployClusterviewApp; import static oracle.weblogic.kubernetes.utils.CommonLBTestUtils.checkIngressReady; import static oracle.weblogic.kubernetes.utils.CommonLBTestUtils.createMultipleDomainsSharingPVUsingWlstAndVerify; @@ -254,6 +255,12 @@ public void tearDownAll() throws ApiException { deletePersistentVolume(pvPvcNamePair.get(0)); } } + if (nginxHelmParams != null && OKE_CLUSTER) { + assertThat(uninstallNginx(nginxHelmParams.getHelmParams())) + .as("Test uninstallNginx returns true") + .withFailMessage("uninstallNginx() did not return true") + .isTrue(); + } } private static void createCertKeyFiles(String cn) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java index 67938a8b7f2..3c92e99cef1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java @@ -32,6 +32,7 @@ import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OCNE; +import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; @@ -43,6 +44,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolumeClaim; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; +import static oracle.weblogic.kubernetes.actions.TestActions.uninstallTraefik; import static oracle.weblogic.kubernetes.utils.CommonLBTestUtils.buildAndDeployClusterviewApp; import static oracle.weblogic.kubernetes.utils.CommonLBTestUtils.createMultipleDomainsSharingPVUsingWlstAndVerify; import static oracle.weblogic.kubernetes.utils.CommonLBTestUtils.verifyAdminServerAccess; @@ -53,6 +55,7 @@ import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithTLSCertKey; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -65,7 +68,7 @@ @IntegrationTest @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("oke-sequential") +@Tag("oke-weekly-sequential") class ItLBTwoDomainsTraefik { private static final int numberOfDomains = 2; @@ -219,6 +222,13 @@ public void tearDownAll() throws ApiException { deletePersistentVolume(pvPvcNamePair.get(0)); } } + if (traefikHelmParams != null && OKE_CLUSTER) { + + assertThat(uninstallTraefik(traefikHelmParams)) + .as("Test uninstallTraefik returns true") + .withFailMessage("uninstallTraefik did not return true") + .isTrue(); + } } private static void createCertKeyFiles(String cn) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java index f3a6b2a4c1f..df58ccacc2b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSideCar.java @@ -101,7 +101,7 @@ + "MonitoringExporter Side Car via Prometheus and Grafana") @IntegrationTest @Tag("olcne-mrg") -@Tag("oke-sequential") +@Tag("oke-weekly-sequential") @Tag("kind-parallel") @Tag("okd-wls-mrg") class ItMonitoringExporterSideCar { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java index cd0ef988bfa..ca0e3f43a4e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRemoteConsole.java @@ -62,6 +62,8 @@ import static oracle.weblogic.kubernetes.actions.TestActions.getServicePort; import static oracle.weblogic.kubernetes.actions.TestActions.installWlsRemoteConsole; import static oracle.weblogic.kubernetes.actions.TestActions.listIngresses; +import static oracle.weblogic.kubernetes.actions.TestActions.uninstallNginx; +import static oracle.weblogic.kubernetes.actions.TestActions.uninstallTraefik; import static oracle.weblogic.kubernetes.actions.impl.Service.getServiceNodePort; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReady; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.callWebAppAndWaitTillReturnedCode; @@ -96,7 +98,7 @@ @DisabledOnSlimImage @Tag("olcne-mrg") @Tag("kind-parallel") -@Tag("oke-parallel") +@Tag("oke-weekly-sequential") class ItRemoteConsole { private static String domainNamespace = null; @@ -311,6 +313,19 @@ public void tearDownAll() { if (!SKIP_CLEANUP) { assertTrue(shutdownWlsRemoteConsole(), "Remote Console shutdown failed"); } + if (traefikHelmParams != null && OKE_CLUSTER) { + + assertThat(uninstallTraefik(traefikHelmParams)) + .as("Test uninstallTraefik returns true") + .withFailMessage("uninstallTraefik did not return true") + .isTrue(); + } + if (nginxHelmParams != null && OKE_CLUSTER) { + assertThat(uninstallNginx(nginxHelmParams.getHelmParams())) + .as("Test uninstallNginx returns true") + .withFailMessage("uninstallNginx() did not return true") + .isTrue(); + } } private static void createTraefikIngressRoutingRules(String domainNamespace) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java index a4f31a0b9af..82030246c36 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java @@ -784,12 +784,12 @@ public static void verifyClusterLoadbalancing(String domainUid, + "&password=" + ADMIN_PASSWORD_DEFAULT + ((host != null) && host.contains(":") ? "&ipv6=true" : "&ipv6=false") + "\""; if (hostRouting) { - curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' " + curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' -v " + "-H 'host: %s' %s://%s/" + uri, ingressHostName, protocol, host) : String.format("curl -g --show-error -ks --noproxy '*' " + "-H 'host: %s' %s://%s/" + uri, ingressHostName, protocol, getHostAndPort(host, lbPort)); } else { - curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' " + curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' -v " + "%s://%s" + locationString + "/" + uri, protocol, host) : String.format("curl -g --show-error -ks --noproxy '*' " + "%s://%s" + locationString + "/" + uri, protocol, getHostAndPort(host, lbPort)); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index a186be4793e..d5ac4d31da6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -386,10 +386,29 @@ public static String getLoadBalancerIP(String namespace, String lbName, boolean return null; } - private static boolean checkLoadBalancerHealthy(String namespace, String lbServiceName) { + /** + * Update NO_PROXY var with Load Balancer IP address. + * + * @param newEntry value to add for NO_PROXY + * @throws Exception throws exception if failed to update + */ + public static void addNoProxyEntry(String newEntry) { + String currentNoProxy = System.getenv("NO_PROXY"); + getLogger().info("Current NO_PROXY value is :" + currentNoProxy); + + String updatedNoProxy = (currentNoProxy == null || currentNoProxy.isEmpty()) + ? newEntry + : currentNoProxy + ",10.196.0.0/24,10.196.1.0/24," + newEntry; + + System.setProperty("NO_PROXY", updatedNoProxy); + getLogger().info("Updated NO_PROXY: " + System.getProperty("NO_PROXY")); + } + + private static synchronized boolean checkLoadBalancerHealthy(String namespace, String lbServiceName) { String lbPublicIP = assertDoesNotThrow(() -> getLoadBalancerIP(namespace, lbServiceName)); InitializationTasks.registerLoadBalancerExternalIP(lbPublicIP); + assertDoesNotThrow(() -> addNoProxyEntry(lbPublicIP)); LoggingFacade logger = getLogger(); String testcompartmentid = System.getProperty("wko.it.oci.compartment.ocid"); logger.info("wko.it.oci.compartment.ocid property " + testcompartmentid); @@ -675,7 +694,8 @@ public static List createIngressForDomainAndVerify(String domainUid, if (host.contains(":")) { host = "[" + host + "]"; } - String curlCmd = "curl -g --silent --show-error --noproxy '*' -H 'host: " + ingressHost + String curlCmd = "curl -g --silent --show-error --noproxy '*' " + + " -v --max-time 60 -H 'host: " + ingressHost + "' http://" + getHostAndPort(host, nodeport) + "/weblogic/ready --write-out %{http_code} -o /dev/null"; @@ -739,7 +759,8 @@ public static void createNginxIngressPathRoutingRules(String domainNamespace, logger.info("ingress {0} was created in namespace {1}", ingressName, domainNamespace); // check the ingress is ready to route the app to the server pod - String curlCmd = "curl -g --silent --show-error --noproxy '*' http://" + hostAndPort + String curlCmd = "curl -g --silent --show-error --noproxy '*' " + + " -v --max-time 60 http://" + hostAndPort + "/weblogic/ready --write-out %{http_code} -o /dev/null"; logger.info("Executing curl command {0}", curlCmd); @@ -803,7 +824,8 @@ public static List createTraefikIngressForDomainAndVerify( if (host.contains(":")) { host = "[" + host + "]"; } - String curlCmd = "curl -g --silent --show-error --noproxy '*' -H 'host: " + ingressHost + String curlCmd = "curl -g --silent --show-error --noproxy '*' " + + " -v --max-time 60 -H 'host: " + ingressHost + "' http://" + host + ":" + nodeport + "/weblogic/ready --write-out %{http_code} -o /dev/null"; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index f7851aabf85..d9f11fcb628 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -256,7 +256,8 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa LoggingFacade logger = getLogger(); // url String curlCmd = - String.format("curl -g --silent --show-error --noproxy '*' -H 'host: *'" + String.format("curl -g --silent --show-error --noproxy '*' -v " + + " --max-time 60 -H 'host: *'" + " http://%s/api/v1/query?query=%s", hostPortPrometheus, searchKey); @@ -302,7 +303,7 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa LoggingFacade logger = getLogger(); // url String curlCmd = - String.format("curl -g --silent --show-error --noproxy '*' -H 'host: " + ingressHost + "'" + String.format("curl -g --silent --show-error --noproxy '*' -v -H 'host: " + ingressHost + "'" + " http://%s/api/v1/query?query=%s", hostPortPrometheus, searchKey); @@ -1229,7 +1230,8 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli host = InetAddress.getLocalHost().getHostAddress(); } String curlCmd = - String.format("curl -g --silent --show-error --noproxy '*' -H 'host: %s' http://%s:%s@%s:%s/wls-exporter/metrics", + String.format("curl -g --silent --show-error --noproxy '*' -v " + + " --max-time 60 -H 'host: %s' http://%s:%s@%s:%s/wls-exporter/metrics", nginxHost, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, @@ -1258,7 +1260,8 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli // check that NGINX can access the sample apps from all managed servers in the domain String curlCmd = - String.format("curl -g --silent --show-error --noproxy '*' -H 'host: %s' http://%s:%s@%s/wls-exporter/metrics", + String.format("curl -g --silent --show-error --noproxy '*' -v " + + " --max-time 60 -H 'host: %s' http://%s:%s@%s/wls-exporter/metrics", nginxHost, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, @@ -1285,7 +1288,8 @@ public static void verifyMonExpAppAccess(int replicaCount, String hostPort) { // check the access to monitoring exporter apps from all managed servers in the domain String curlCmd = - String.format("curl -g --silent --show-error --noproxy '*' http://%s:%s@%s/wls-exporter/metrics", + String.format("curl -g --silent --show-error --noproxy '*' -v " + + " --max-time 60 http://%s:%s@%s/wls-exporter/metrics", ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, hostPort); From 757b762c16340681571bcff3adeb7550084dd17b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 13 Nov 2024 14:42:55 +0000 Subject: [PATCH 223/356] Merge branch 'rm/PCAsupport' into 'main' Update PCA support See merge request weblogic-cloud/weblogic-kubernetes-operator!4863 (cherry picked from commit c2fbe49595e0484fbd6c0adb5c8e24b375d221fa) 584ef874 update PCA support --- .../site/content/introduction/platforms/environments.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/site/content/introduction/platforms/environments.md b/documentation/site/content/introduction/platforms/environments.md index 9bf97f3438d..1a1ee418e8b 100644 --- a/documentation/site/content/introduction/platforms/environments.md +++ b/documentation/site/content/introduction/platforms/environments.md @@ -105,7 +105,9 @@ Oracle Exalogic Elastic Cloud systems can be migrated to this infrastructure wit changes, enabling you to preserve your application investment as you adopt modern cloud native infrastructure. -WebLogic Kubernetes Operator is certified for use on PCA X9 with Kubernetes 1.24.5+ and Istio 1.14. +* WebLogic Kubernetes Operator is certified for use on PCA X9 with Kubernetes 1.24.5+ and Istio 1.14. + +* WebLogic Kubernetes Operator 4.x is certified for use on PCA v3. For more information, see [Using the OKE Service](https://docs.oracle.com/en/engineered-systems/private-cloud-appliance/3.0-latest/oke/oke-overview.html) in the Oracle Private Cloud Appliance Container Engine for Kubernetes documentation. #### Microsoft Azure From 5613c79bc786a5948e75158c545cf5ddb86e0ff9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 13 Nov 2024 11:27:59 -0500 Subject: [PATCH 224/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5f1ed255678..3322a98f896 100644 --- a/pom.xml +++ b/pom.xml @@ -685,15 +685,15 @@ 3.4.2 3.3.1 3.21.0 - 3.5.1 + 3.5.2 3.6.0 3.1.1 - 3.10.1 - 3.5.1 + 3.11.1 + 3.5.2 3.8.1 3.6.0 3.5.0 - 10.20.0 + 10.20.1 1.0 3.6.0 3.2.7 @@ -740,7 +740,7 @@ 1.5.12 4.28.3 2.5.1 - 9.44 + 9.46 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 1388da83067d9156de8faeef10993ede1d3d724b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 14 Nov 2024 15:10:40 -0500 Subject: [PATCH 225/356] Prepare for WKO 4.2.11 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 1b35d66f8d4..0b1585088ab 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 1313cd9a315..510b1749cad 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index f5d499fa7eb..5c43a05bdbc 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index c5f3af80459..75f2b982bde 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.11-SNAPSHOT + 4.2.11 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 035cc2b0dfd..834a0f72339 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 75f763d2c50..45f94c8ba2d 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.11-SNAPSHOT + 4.2.11 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 701b9376d9a..12fbf28eaf5 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 3322a98f896..63d49edfe72 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 6085572d9c2..c6472ac4c22 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11-SNAPSHOT + 4.2.11 operator-swagger From 4bcc556766674bee2f824897d32ea7c33c8b94f9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 14 Nov 2024 15:34:04 -0500 Subject: [PATCH 226/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 0b1585088ab..2e1dfbf47a7 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 510b1749cad..491aa66ff6a 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 5c43a05bdbc..66bcf771594 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 75f2b982bde..b03290500a6 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.11 + 4.2.12-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 834a0f72339..0da8084ba32 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 45f94c8ba2d..8a27b75a42c 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.11 + 4.2.12-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 12fbf28eaf5..0f72cab76e3 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 63d49edfe72..84a1172cd57 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index c6472ac4c22..eafe6877497 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.11 + 4.2.12-SNAPSHOT operator-swagger From 430931295d67f2714f7f5bb0333440f91ede617d Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Thu, 14 Nov 2024 21:40:07 +0000 Subject: [PATCH 227/356] Added check for check health status for unknown-state-backend-set-names during Load Balancer creation on OCI for OKE tests --- .../kubernetes/utils/LoadBalancerUtils.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java index d5ac4d31da6..8525de4d58f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/LoadBalancerUtils.java @@ -473,8 +473,23 @@ private static synchronized boolean checkLoadBalancerHealthy(String namespace, S return false; } - return result.stdout().contains("OK"); + + return (result.stdout().contains("OK") && isBackendHealthy(result.stdout())); + } + + private static boolean isBackendHealthy(String jsonResponse) { + LoggingFacade logger = getLogger(); + // Check for any non-empty backend set names indicating a failure + if (jsonResponse.contains("\"critical-state-backend-set-names\": []") + && jsonResponse.contains("\"unknown-state-backend-set-names\": []") + && jsonResponse.contains("\"warning-state-backend-set-names\": []")) { + logger.info("All backends are healthy."); + return true; // Healthy + } else { + logger.severe("Failure: There are issues with the backend(s)." + jsonResponse); + return false; // Unhealthy + } } @Nullable From 806b56862af1f43cd781da9b473516e43cbbd914 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Mon, 18 Nov 2024 21:53:34 +0000 Subject: [PATCH 228/356] Reuser RCU to restart FMW domain on PV --- .../ItFmwDomainInPvUserCreateRcu.java | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java index 9e92cdb35c4..991bb42996e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java @@ -71,7 +71,6 @@ import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.deleteDomainResource; import static oracle.weblogic.kubernetes.utils.FmwUtils.createDomainResourceSimplifyJrfPv; -import static oracle.weblogic.kubernetes.utils.FmwUtils.createSimplifyJrfPvDomainAndRCU; import static oracle.weblogic.kubernetes.utils.FmwUtils.saveAndRestoreOpssWalletfileSecret; import static oracle.weblogic.kubernetes.utils.FmwUtils.verifyDomainReady; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; @@ -79,6 +78,8 @@ import static oracle.weblogic.kubernetes.utils.JobUtils.getIntrospectJobName; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchDomainResourceServerStartPolicy; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDeleted; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDoesNotExist; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodLogContains; @@ -595,34 +596,18 @@ void testFmwDomainOnPvUserCreatesRCUMultiImages() { /** * Export the OPSS wallet file secret of Fmw domain from the previous run * CrateIfNotExists set to DomainAndRCU - * Use this OPSS wallet file secret to create Fmw domain on PV to connect to the same database + * Use this OPSS wallet file secret to restart Fmw domain on PV to connect to the same database * Verify Pod is ready and service exists for both admin server and managed servers. */ @Test @Order(6) - @DisplayName("Create a FMW domain on PV with provided OPSS wallet file secret") - void testFmwDomainOnPVwithProvidedOpss() { - - final String pvName = getUniqueName(domainUid3 + "-pv-"); - final String pvcName = getUniqueName(domainUid3 + "-pvc-"); + @DisplayName("Restart a FMW domain on PV with provided OPSS wallet file secret") + void testReuseRCUschemaToRestartFmwDomain() { saveAndRestoreOpssWalletfileSecret(domainNamespace, domainUid3, opsswalletfileSecretName3); - logger.info("Deleting domain custom resource with namespace: {0}, domainUid {1}", domainNamespace, domainUid3); - deleteDomainResource(domainNamespace, domainUid3); - try { - deleteDirectory(Paths.get("/shared").toFile()); - } catch (IOException ioe) { - logger.severe("Failed to cleanup directory /shared", ioe); - } - logger.info("Creating domain custom resource with pvName: {0}", pvName); - DomainResource domain = createSimplifyJrfPvDomainAndRCU( - domainUid3, domainNamespace, adminSecretName3, - TEST_IMAGES_REPO_SECRET_NAME, - rcuaccessSecretName3, - opsswalletpassSecretName3, opsswalletfileSecretName3, - pvName, pvcName, domainCreationImages3, null); - - createDomainAndVerify(domain, domainNamespace); + shutdownDomain(domainUid3); + patchDomainWithWalletFileSecret(opsswalletfileSecretName3, domainUid3); + startupDomain(domainUid3); // verify that all servers are ready verifyDomainReady(domainNamespace, domainUid3, replicaCount, "nosuffix"); @@ -892,5 +877,56 @@ private static void createModelConfigMap(String domainid, String cfgMapName) { assertTrue(cmCreated, String.format("createConfigMap failed %s", cfgMapName)); } + /** + * Shutdown the domain by setting serverStartPolicy as "Never". + */ + private void shutdownDomain(String domainUid) { + patchDomainResourceServerStartPolicy("/spec/serverStartPolicy", "Never", domainNamespace, domainUid); + logger.info("Domain is patched to stop entire WebLogic domain"); + + String adminServerPodName = domainUid + "-admin-server"; + String managedServerPrefix = domainUid + "-managed-server"; + + // make sure all the server pods are removed after patch + checkPodDeleted(adminServerPodName, domainUid, domainNamespace); + for (int i = 1; i <= replicaCount; i++) { + checkPodDeleted(managedServerPrefix + i, domainUid, domainNamespace); + } + + logger.info("Domain shutdown success"); + + } + + /** + * Startup the domain by setting serverStartPolicy as "IfNeeded". + */ + private void startupDomain(String domainUid) { + patchDomainResourceServerStartPolicy("/spec/serverStartPolicy", "IfNeeded", domainNamespace, + domainUid); + logger.info("Domain is patched to start all servers in the domain"); + } + + /** + * Patch the domain with opss wallet file secret. + * @param opssWalletFileSecretName the name of opps wallet file secret + * @return true if patching succeeds, false otherwise + */ + private boolean patchDomainWithWalletFileSecret(String opssWalletFileSecretName, String domainUid) { + // construct the patch string for adding server pod resources + StringBuffer patchStr = new StringBuffer("[{") + .append("\"op\": \"add\", ") + .append("\"path\": \"/spec/configuration/opss/walletFileSecret\", ") + .append("\"value\": \"") + .append(opssWalletFileSecretName) + .append("\"}]"); + + logger.info("Adding opssWalletPasswordSecretName for domain {0} in namespace {1} using patch string: {2}", + domainUid, domainNamespace, patchStr.toString()); + + V1Patch patch = new V1Patch(new String(patchStr)); + + return patchDomainCustomResource(domainUid, domainNamespace, patch, V1Patch.PATCH_FORMAT_JSON_PATCH); + } + } \ No newline at end of file From f2194e587f2d74eea85e70cd52c602f0ef263ea6 Mon Sep 17 00:00:00 2001 From: jshum Date: Tue, 19 Nov 2024 21:16:47 -0600 Subject: [PATCH 229/356] Fix domain library capture logic --- operator/src/main/resources/scripts/modelInImage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index bc9bf2d6b19..48098b99298 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -1102,7 +1102,7 @@ wdtUpdateModelDomain() { captureBinLibAdded() { local BINLIBDIR_NAME="/tmp/binlibdir.txt" find $DOMAIN_HOME/bin -maxdepth 1 -type f | sed "s|$DOMAIN_HOME/bin|wlsdeploy/domainBin|g" > $BINLIBDIR_NAME - find $DOMAIN_HOME/lib -maxdepth 1 -type f | sed "s|$DOMAIN_HOME/bin|wlsdeploy/domainLibraries|g" >> $BINLIBDIR_NAME + find $DOMAIN_HOME/lib -maxdepth 1 -type f | sed "s|$DOMAIN_HOME/lib|wlsdeploy/domainLibraries|g" >> $BINLIBDIR_NAME } wdtHandleOnlineUpdate() { From 614cde169f3e523827e72dfa3387b8b358c1f6a7 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Wed, 20 Nov 2024 14:35:56 +0000 Subject: [PATCH 230/356] Add test cross domain transaction between k8s domain and external domain using WLS 14.1.2.0.0 --- Jenkinsfile.podman | 4 + integration-tests/pom.xml | 39 +- .../ItOnPremCrossDomainTransaction.java | 597 ++++++++++++++++++ .../weblogic/kubernetes/TestConstants.java | 3 + .../weblogic/kubernetes/utils/ImageUtils.java | 11 +- .../kubernetes/utils/OracleHttpClient.java | 63 ++ .../resources/bash-scripts/install-wls.sh | 80 +++ .../resources/onpremcrtx/model-cdt-jms.yaml | 49 ++ ...-crossdomaintransaction-domain1.properties | 7 + .../model-crossdomaintransaction-domain1.yaml | 42 ++ ...-crossdomaintransaction-domain2.properties | 6 + .../model-crossdomaintransaction-domain2.yaml | 30 + .../resources/onpremcrtx/model2-cdt-jms.yaml | 34 + .../onpremcrtx/onprem-domain-routing.yaml | 30 + .../src/test/resources/onpremcrtx/shutdown.py | 11 + 15 files changed, 1000 insertions(+), 6 deletions(-) create mode 100644 integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java create mode 100644 integration-tests/src/test/resources/bash-scripts/install-wls.sh create mode 100644 integration-tests/src/test/resources/onpremcrtx/model-cdt-jms.yaml create mode 100644 integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties create mode 100644 integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml create mode 100644 integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.properties create mode 100644 integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.yaml create mode 100644 integration-tests/src/test/resources/onpremcrtx/model2-cdt-jms.yaml create mode 100644 integration-tests/src/test/resources/onpremcrtx/onprem-domain-routing.yaml create mode 100644 integration-tests/src/test/resources/onpremcrtx/shutdown.py diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index fcf3f71d74d..bf1b8fd4308 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -467,6 +467,8 @@ nodes: hostPort: 2224 - containerPort: 31128 hostPort: 2228 + - containerPort: 31132 + hostPort: 8001 extraMounts: - hostPath: ${pv_root} containerPath: ${pv_root} @@ -567,6 +569,7 @@ EOF echo "-DWLSIMG_BUILDER=\"podman\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Djdk.httpclient.allowRestrictedHeaders=\"host\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dinstall.weblogic=\"true\"" >> ${WORKSPACE}/.mvn/maven.config echo "${WORKSPACE}/.mvn/maven.config contents:" cat "${WORKSPACE}/.mvn/maven.config" @@ -586,6 +589,7 @@ EOF export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" + export SHIPHOME_DOWNLOAD_SERVER="home.us.oracle.com" if ! time mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/kindtest.log"; then echo "integration-tests failed" exit 1 diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 66bcf771594..0b3212ac5e2 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -80,6 +80,7 @@ ${env.IMAGE_TAG_OPERATOR} ${env.RELEASE_IMAGE_OPERATOR} ${env.DOCKER_IMAGE} + ${env.SHIPHOME_DOWNLOAD_SERVER} **/It* @@ -770,6 +771,42 @@ false v8o - + + + + integration-test-env + + + + install.weblogic + true + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-silent-install-script + pre-integration-test + + exec + + + bash + + ${project.basedir}/src/test/resources/bash-scripts/install-wls.sh + ${wko.it.result.root} + ${wko.it.shiphome.download.server} + + + + + + + + diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java new file mode 100644 index 00000000000..8b53ac4df7e --- /dev/null +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java @@ -0,0 +1,597 @@ +// Copyright (c) 2024, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.weblogic.kubernetes; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.domain.DomainSpec; +import oracle.weblogic.domain.Model; +import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.Command; +import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; +import oracle.weblogic.kubernetes.annotations.IntegrationTest; +import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecResult; +import oracle.weblogic.kubernetes.utils.OracleHttpClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_BASE; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.DOWNLOAD_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; +import static oracle.weblogic.kubernetes.actions.TestActions.createDomainCustomResource; +import static oracle.weblogic.kubernetes.assertions.TestAssertions.domainExists; +import static oracle.weblogic.kubernetes.utils.ApplicationUtils.checkAppIsActive; +import static oracle.weblogic.kubernetes.utils.BuildApplication.buildApplication; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.formatIPv6Host; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; +import static oracle.weblogic.kubernetes.utils.FileUtils.copyFolder; +import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createImageAndVerify; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; +import static oracle.weblogic.kubernetes.utils.ImageUtils.imageRepoLoginAndPushImageToRegistry; +import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyTraefik; +import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Cross domain transaction tests. + */ +@DisplayName("Verify cross domain transaction across K8S and on-premise is successful") +@IntegrationTest +@Tag("kind-sequential") +class ItOnPremCrossDomainTransaction { + + private static final String WDT_MODEL_FILE_DOMAIN1 = "model-crossdomaintransaction-domain1.yaml"; + private static final String WDT_MODEL_FILE_DOMAIN2 = "model-crossdomaintransaction-domain2.yaml"; + + private static final String WDT_MODEL_DOMAIN1_PROPS = "model-crossdomaintransaction-domain1.properties"; + private static final String WDT_MODEL_DOMAIN2_PROPS = "model-crossdomaintransaction-domain2.properties"; + private static final String ONPREM_DOMAIN_ROUTING = "onprem-domain-routing.yaml"; + private static String onpremIngressClass = null; + private static final String WDT_MODEL_FILE_JMS = "model-cdt-jms.yaml"; + private static final String WDT_MODEL_FILE_JMS2 = "model2-cdt-jms.yaml"; + + private static final String WDT_IMAGE_NAME1 = "domain1-onprem-wdt-image"; + private static final String PROPS_TEMP_DIR = RESULTS_ROOT + "/crossdomainonpremtemp"; + + private static String opNamespace = null; + private static String domain1Namespace = null; + private static String domainUid1 = "domain1"; + private static String adminServerName = "admin-server"; + private static String managedServerPrefix = domainUid1 + "-managed-server"; + private static LoggingFacade logger = null; + private static String hostHeader; + private static Map headers = null; + private static String hostAndPort = null; + + private static String javaOptions = "-Dweblogic.Debug.DebugNaming=true " + + "-Dweblogic.Debug.DebugJTANaming=true " + + "-Dweblogic.debug.DebugConnection=true " + + "-Dweblogic.debug.DebugRouting=true " + + "-Dweblogic.debug.DebugMessaging=true " + + "-Dweblogic.kernel.debug=true " + + "-Dweblogic.log.LoggerSeverity=Debug " + + "-Dweblogic.log.LogSeverity=Debug " + + "-Dweblogic.StdoutDebugEnabled=true " + + "-Dweblogic.log.StdoutSeverity=Debug " + + "-Dweblogic.rjvm.allowUnknownHost=true " + + "-Dweblogic.security.remoteAnonymousRMIT3Enabled=true"; + private static Path wlstScript; + private static Path domainHome; + private static Path mwHome; + + /** + * Install Operator. + * + * @param namespaces list of namespaces + */ + @BeforeAll + public static void initAll(@Namespaces(2) List namespaces) + throws UnknownHostException, IOException, InterruptedException { + logger = getLogger(); + + // get a new unique opNamespace + logger.info("Creating unique namespace for Operator"); + assertNotNull(namespaces.get(0), "Namespace list is null"); + opNamespace = namespaces.get(0); + + logger.info("Creating unique namespace for Domain"); + assertNotNull(namespaces.get(1), "Namespace list is null"); + domain1Namespace = namespaces.get(1); + //install traefil for on prem domain + onpremIngressClass = installTraefikForPremDomain(); + + // Now that we got the namespaces for both the domains, we need to update the model properties + // file with the namespaces. For a cross-domain transaction to work, we need to have the externalDNSName + // set in the config file. Cannot set this after the domain is up since a server restart is + // required for this to take effect. So, copying the property file to RESULT_ROOT and updating the + // property file + updatePropertyFiles(); + createOnPremDomain(); + createOnPremDomainRoutingRules(); + modifyDNS(); + + + // install and verify operator + installAndVerifyOperator(opNamespace, domain1Namespace); + createK8sDomain(); + } + + /** + * Stop on premise domain. + */ + @AfterAll + public static void stopOnPremDomain() throws UnknownHostException { + shutdownServers(List.of(wlstScript.toString(), + Path.of(RESOURCE_DIR, "onpremcrtx").toString() + "/shutdown.py", + getExternalDNSName()), + Path.of(domainHome.toString(), "wlst.log")); + } + + /** + * This test verifies cross-domain MessageDrivenBean communication A transacted MDB on Domain D1 listen on a + * replicated Distributed Topic on Domain D2. The MDB is deployed to cluster on domain D1 with + * MessagesDistributionMode set to One-Copy-Per-Server. The OnMessage() routine sends a message to local queue on + * receiving the message. An application servlet is deployed to Administration Server on D1 which send/receive message + * from a JMS destination based on a given URL. (a) app servlet send message to Distributed Topic on D2 (b) mdb puts a + * message into local Queue for each received message (c) make sure local Queue gets 2X times messages sent to + * Distributed Topic Since the MessagesDistributionMode is set to One-Copy-Per-Server and targeted to a cluster of two + * servers, onMessage() will be triggered for both instance of MDB for a message sent to Distributed Topic + */ + @Test + @DisplayName("Check cross domain transcated MDB communication ") + void testCrossDomainTranscatedMDB() throws IOException, InterruptedException { + + // No extra header info + String curlHostHeader = ""; + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + curlHostHeader = "--header 'Host: " + hostHeader + "'"; + } + assertTrue(checkAppIsActive(hostAndPort, + curlHostHeader, "mdbtopic", "cluster-1", + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), + "MDB application can not be activated on domain1/cluster"); + + logger.info("MDB application is activated on domain1/cluster"); + + //since JMS provider is clustered instances and since they are running on-prem + //cluster address is not available. Hence sending messages to individual instances + List ports = List.of("8002", "8003"); + for (String port : ports) { + String url = String.format("http://%s/jmsservlet/jmstest?" + + "url=t3://%s:%s&" + + "cf=jms.ClusterConnectionFactory&" + + "action=send&" + + "dest=jms/testCdtUniformTopic", + hostAndPort, getExternalDNSName(), port); + logger.info(url); + + HttpResponse response; + response = OracleHttpClient.get(url, headers, true); + assertEquals(200, response.statusCode(), "Didn't get the 200 HTTP status"); + assertTrue(response.body().contains("Sent (10) message"), + "Can not send message to remote Distributed Topic"); + } + + assertTrue(checkLocalQueue(), + "Expected number of message not found in Accounting Queue"); + } + + private boolean checkLocalQueue() { + String url = String.format("http://%s/jmsservlet/jmstest?" + + "url=t3://localhost:7001&" + + "action=receive&dest=jms.testAccountingQueue", + hostAndPort); + + logger.info("Queue check url {0}", url); + testUntil(() -> { + HttpResponse response; + response = OracleHttpClient.get(url, headers, true); + return response.statusCode() == 200 + && response.body().contains("Recorded (20) message from [managed-server"); + }, logger, "local queue to be updated"); + + testUntil(() -> { + HttpResponse response; + response = OracleHttpClient.get(url, headers, true); + return response.statusCode() == 200 + && (response.body().contains("Recorded (0) message from [managed-server1]") + || response.body().contains("Recorded (0) message from [managed-server2]")); + }, logger, "destination topic to be consumed"); + return true; + } + + + private static void updatePropertyFiles() { + //create a temporary directory to copy and update the properties file + Path target = Path.of(PROPS_TEMP_DIR); + Path source1 = Path.of(RESOURCE_DIR, "onpremcrtx", WDT_MODEL_DOMAIN1_PROPS); + Path source2 = Path.of(RESOURCE_DIR, "onpremcrtx", WDT_MODEL_DOMAIN2_PROPS); + Path source3 = Path.of(RESOURCE_DIR, "onpremcrtx", ONPREM_DOMAIN_ROUTING); + Path domain1Properties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN1_PROPS); + Path domain2Properties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN2_PROPS); + Path onPremDomainRouting = Path.of(PROPS_TEMP_DIR, ONPREM_DOMAIN_ROUTING); + logger.info("Copy the properties file to the above area so that we can add namespace property"); + assertDoesNotThrow(() -> { + Files.createDirectories(target); + Files.copy(source1, domain1Properties, StandardCopyOption.REPLACE_EXISTING); + Files.copy(source2, domain2Properties, StandardCopyOption.REPLACE_EXISTING); + //Files.copy(source3, onPremDomainRouting, StandardCopyOption.REPLACE_EXISTING); + Files.writeString(domain1Properties, "\nNAMESPACE=" + domain1Namespace, StandardOpenOption.APPEND); + Files.writeString(domain2Properties, "\nDNS_NAME=" + getExternalDNSName(), StandardOpenOption.APPEND); + String content = new String(Files.readAllBytes(source3), StandardCharsets.UTF_8); + Files.write(onPremDomainRouting, + content.replaceAll("NAMESPACE", domain1Namespace) + .replaceAll("traefik-onprem", onpremIngressClass) + .getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + }); + } + + private static void createK8sDomain() throws UnknownHostException, IOException { + + List applicationsList = buildApplications(); + // create admin credential secret for domain1 + logger.info("Create admin credential secret for domain1"); + String domain1AdminSecretName = domainUid1 + "-weblogic-credentials"; + assertDoesNotThrow(() -> createSecretWithUsernamePassword( + domain1AdminSecretName, domain1Namespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), + String.format("createSecret %s failed for %s", domain1AdminSecretName, domainUid1)); + + // build the model file list for domain1 + final List modelFilesListDomain1 = Arrays.asList( + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN1, + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS); + + logger.info("Creating image with model file and verify"); + String domain1Image = createImageAndVerify(WDT_IMAGE_NAME1, modelFilesListDomain1, + applicationsList, WDT_MODEL_DOMAIN1_PROPS, PROPS_TEMP_DIR, domainUid1); + logger.info("Created {0} image", domain1Image); + + // repo login and push image to registry if necessary + imageRepoLoginAndPushImageToRegistry(domain1Image); + + //create domain1 + createDomain(domainUid1, domain1Namespace, domain1AdminSecretName, domain1Image); + + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + hostHeader = createIngressHostRouting(domain1Namespace, domainUid1, adminServerName, 7001); + hostAndPort = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) + + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT; + headers = new HashMap<>(); + headers.put("host", hostHeader); + } + } + + private static List buildApplications() { + //build jmsservlet client application archive + Path targetDir; + Path distDir; + //build application archive for JMS Send/Receive + targetDir = Paths.get(WORK_DIR, + ItOnPremCrossDomainTransaction.class.getName() + "/jmsservlet"); + distDir = buildApplication(Paths.get(APP_DIR, "jmsservlet"), null, null, + "build", domain1Namespace, targetDir); + logger.info("distDir is {0}", distDir.toString()); + assertTrue(Paths.get(distDir.toString(), + "jmsservlet.war").toFile().exists(), + "Application archive is not available"); + String appSource1 = distDir.toString() + "/jmsservlet.war"; + logger.info("Application is in {0}", appSource1); + + //build the MDB application + Path mdbSrcDir = Paths.get(APP_DIR, "mdbtopic"); + Path mdbDestDir = Paths.get(PROPS_TEMP_DIR, "mdbtopic"); + assertDoesNotThrow(() -> copyFolder( + mdbSrcDir.toString(), mdbDestDir.toString()), + "Could not copy mdbtopic application directory"); + Path template = Paths.get(PROPS_TEMP_DIR, + "mdbtopic/src/application/MdbTopic.java"); + // Add the external ip addresses of the on-premise cluster instances + // so that it can communicate with remote destination on domain2 + assertDoesNotThrow(() -> replaceStringInFile( + template.toString(), "t3://domain2-cluster-cluster-1.domain2-namespace:8001", + "t3://" + getExternalDNSName() + ":8002," + getExternalDNSName() + ":8003"), + "Could not modify the provider url in MDB Template file"); + //build application archive for MDB + targetDir = Paths.get(WORK_DIR, + ItOnPremCrossDomainTransaction.class.getName() + "/mdbtopic"); + distDir = buildApplication(Paths.get(PROPS_TEMP_DIR, "mdbtopic"), null, null, + "build", domain1Namespace, targetDir); + logger.info("distDir is {0}", distDir.toString()); + assertTrue(Paths.get(distDir.toString(), + "mdbtopic.jar").toFile().exists(), + "Application archive is not available"); + String appSource2 = distDir.toString() + "/mdbtopic.jar"; + logger.info("Application is in {0}", appSource2); + return List.of(appSource1, appSource2); + } + + private static void createDomain(String domainUid, + String domainNamespace, + String adminSecretName, + String domainImage) { + // admin/managed server name here should match with model yaml in WDT_MODEL_FILE + final String adminServerPodName = domainUid + "-admin-server"; + final String managedServerPrefix = domainUid + "-managed-server"; + final int replicaCount = 2; + + // Create the repo secret to pull the image + // this secret is used only for non-kind cluster + createTestRepoSecret(domainNamespace); + + // create the domain CR + createDomainResource(domainUid, domainNamespace, adminSecretName, TEST_IMAGES_REPO_SECRET_NAME, + replicaCount, domainImage); + + // wait for the domain to exist + logger.info("Check for domain custom resource in namespace {0}", domainNamespace); + testUntil( + domainExists(domainUid, DOMAIN_VERSION, domainNamespace), + logger, + "domain {0} to be created in namespace {1}", + domainUid, + domainNamespace + ); + + // check admin server pod exists + // check admin server services created + logger.info("Check for admin server pod {0} existence in namespace {1}", + adminServerPodName, domainNamespace); + checkPodReadyAndServiceExists(adminServerPodName, domainUid, domainNamespace); + // check managed server pods exist + // check managed server services created + for (int i = 1; i <= replicaCount; i++) { + logger.info("Check for managed server pod {0} existence in namespace {1}", + managedServerPrefix + i, domainNamespace); + checkPodReadyAndServiceExists(managedServerPrefix + i, domainUid, domainNamespace); + } + } + + private static void createDomainResource(String domainUid, String domNamespace, String adminSecretName, + String repoSecretName, int replicaCount, String domainImage) { + logger.info("Image to be used is {0}", domainImage); + + // create the domain CR + DomainResource domain = new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new V1ObjectMeta() + .name(domainUid) + .namespace(domNamespace)) + .spec(new DomainSpec() + .domainUid(domainUid) + .replicas(replicaCount) + .domainHomeSourceType("Image") + .image(domainImage) + .imagePullPolicy(IMAGE_PULL_POLICY) + .addImagePullSecretsItem(new V1LocalObjectReference() + .name(repoSecretName)) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(adminSecretName)) + .includeServerOutInPodLog(true) + .serverStartPolicy("IfNeeded") + .serverPod(new ServerPod() + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false " + + "-Dweblogic.debug.DebugJTAXA=true " + + "-Dweblogic.debug.DebugJTA2PC=true " + + "-Dweblogic.security.remoteAnonymousRMIT3Enabled=true " + + "-Dweblogic.rjvm.allowUnknownHost=true ")) + .addEnvItem(new V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom "))) + .configuration(new Configuration() + .model(new Model() + .domainType("WLS")) + .introspectorJobActiveDeadlineSeconds(3000L))); + setPodAntiAffinity(domain); + logger.info("Create domain custom resource for domainUid {0} in namespace {1}", + domainUid, domNamespace); + boolean domCreated = assertDoesNotThrow(() -> createDomainCustomResource(domain), + String.format("Create domain custom resource failed with ApiException for %s in namespace %s", + domainUid, domNamespace)); + assertTrue(domCreated, String.format("Create domain custom resource failed with ApiException " + + "for %s in namespace %s", domainUid, domNamespace)); + } + + private static void createOnPremDomain() throws IOException, InterruptedException { + logger.info("creating on premise domain"); + Path createDomainScript = downloadAndInstallWDT(); + mwHome = Path.of(RESULTS_BASE, "mwhome"); + String modelFileList = RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN2 + "," + + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS2; + domainHome = Path.of(RESULTS_BASE, "mwhome", "domains", "domain2"); + logger.info("creating on premise domain home {0}", domainHome); + Files.createDirectories(domainHome); + Path modelProperties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN2_PROPS); + List command = List.of( + createDomainScript.toString(), + "-oracle_home", mwHome.toString(), + "-domain_type", "WLS", + "-domain_home", domainHome.toString(), + "-model_file", modelFileList, + "-variable_file", modelProperties.toString() + ); + runWDTandCreateDomain(command.stream().collect(Collectors.joining(" "))); + createBootProperties(domainHome.toString()); + startServers(domainHome); + wlstScript = Path.of(mwHome.toString(), "oracle_common", "common", "bin", "wlst.sh"); + } + + private static Path downloadAndInstallWDT() throws IOException { + String wdtUrl = WDT_DOWNLOAD_URL + "/download/weblogic-deploy.zip"; + Path destLocation = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy.zip"); + Path createDomainScript = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy", "bin", "createDomain.sh"); + if (!Files.exists(destLocation) && !Files.exists(createDomainScript)) { + logger.info("Downloading WDT to {0}", destLocation); + Files.createDirectories(destLocation.getParent()); + OracleHttpClient.downloadFile(wdtUrl, destLocation.toString(), null, null, 3); + String cmd = "cd " + destLocation.getParent() + ";unzip " + destLocation; + assertTrue(Command.withParams(new CommandParams().command(cmd)).execute(), "unzip command failed"); + } + + assertTrue(Files.exists(createDomainScript), "could not find createDomain.sh script"); + return createDomainScript; + } + + private static void runWDTandCreateDomain(String command) { + logger.info("running {0}", command); + assertTrue(Command.withParams(new CommandParams().command(command)).execute(), "create domain failed"); + } + + private static String getExternalDNSName() throws UnknownHostException { + return InetAddress.getLocalHost().getHostAddress(); + } + + private static void createBootProperties(String domainHome) throws IOException { + List servers = List.of("admin-server", "managed-server1", "managed-server2"); + for (String server : servers) { + Path securityDir = Files.createDirectories(Path.of(domainHome, "servers", server, "security")); + Path bootFile = Files.createFile(Path.of(securityDir.toString(), "boot.properties")); + logger.info("creating boot.properties {0}", bootFile); + Files.writeString(bootFile, "username=weblogic\n", StandardOpenOption.TRUNCATE_EXISTING); + Files.writeString(bootFile, "password=welcome1\n", StandardOpenOption.APPEND); + assertTrue(Files.exists(bootFile), "failed to create boot.properties file"); + } + } + + private static void startServers(Path domainHome) throws InterruptedException, UnknownHostException { + startWebLogicServer(List.of(domainHome.toString() + "/bin/startWebLogic.sh"), + Path.of(domainHome.toString(), "admin-server.log")); + TimeUnit.SECONDS.sleep(15); + startWebLogicServer(List.of(domainHome.toString() + "/bin/startManagedWebLogic.sh", + "managed-server1", + "t3://" + getExternalDNSName() + ":7001"), + Path.of(domainHome.toString(), "managed-server1.log")); + startWebLogicServer(List.of(domainHome.toString() + "/bin/startManagedWebLogic.sh", + "managed-server2", + "t3://" + getExternalDNSName() + ":7001"), + Path.of(domainHome.toString(), "managed-server2.log")); + TimeUnit.SECONDS.sleep(15); + } + + private static void startWebLogicServer(List command, Path logFile) { + Thread serverThread = new Thread(() -> { + ProcessBuilder processBuilder = new ProcessBuilder(command); + Map combinedEnvMap = new HashMap<>(); + combinedEnvMap.putAll(System.getenv()); + combinedEnvMap.put("JAVA_OPTIONS", javaOptions); + processBuilder.environment().putAll(combinedEnvMap); + processBuilder.redirectError(new File(logFile.toString())); + processBuilder.redirectOutput(new File(logFile.toString())); + try { + logger.info("Starting server with command : {0}", String.join(" ", command)); + Process process = processBuilder.start(); + logger.info("Server is starting..."); + process.waitFor(); // This will wait for the process to complete in the thread + logger.info("Server has shut down."); + } catch (IOException | InterruptedException e) { + logger.info(e.getLocalizedMessage()); + } + }); + serverThread.start(); + } + + private static void shutdownServers(List command, Path logFile) { + Thread serverThread = new Thread(() -> { + ProcessBuilder processBuilder = new ProcessBuilder(command); + Map combinedEnvMap = new HashMap<>(); + combinedEnvMap.putAll(System.getenv()); + processBuilder.environment().putAll(combinedEnvMap); + processBuilder.redirectError(new File(logFile.toString())); + processBuilder.redirectOutput(new File(logFile.toString())); + try { + logger.info("shutting down servers using wlst " + String.join(" ", command)); + Process process = processBuilder.start(); + process.waitFor(); // This will wait for the process to complete in the thread + logger.info("Servers are shut down."); + } catch (IOException | InterruptedException e) { + logger.info(e.getLocalizedMessage()); + } + }); + serverThread.start(); + } + + private static void modifyDNS() throws UnknownHostException, IOException, InterruptedException { + String dnsEntries = getExternalDNSName() + + " " + managedServerPrefix + "1." + domain1Namespace + + " " + managedServerPrefix + "2." + domain1Namespace + + " " + domainUid1 + "-" + adminServerName + "." + domain1Namespace; + String command = "echo \"" + dnsEntries + "\" | sudo tee -a /etc/hosts > /dev/null"; + logger.info("adding DNS entries with command {0}", command); + ExecResult result; + result = exec(command, true); + getLogger().info("The command returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + assertEquals(0, result.exitValue(), "adding DNS entries for on prem domain failed"); + } + + private static void createOnPremDomainRoutingRules() throws IOException, InterruptedException { + String command = KUBERNETES_CLI + " apply -f " + Path.of(PROPS_TEMP_DIR, ONPREM_DOMAIN_ROUTING); + logger.info("creating ingress routing rules for onprem domain \n{0}", command); + ExecResult result; + result = exec(command, true); + getLogger().info("The command returned exit value: " + result.exitValue() + + " command output: " + result.stderr() + "\n" + result.stdout()); + assertEquals(0, result.exitValue(), "creating on prem routing rules failed"); + } + + private static String installTraefikForPremDomain() { + logger.info("installing traefik lb for on prem domain"); + return installAndVerifyTraefik(domain1Namespace, + IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT, 0).getIngressClassName(); + } + +} diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 3da2206696c..86d912758a3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -564,4 +564,7 @@ public interface TestConstants { public static final int IT_REMOTECONSOLENGINX_INGRESS_HTTPS_NODEPORT = 31128; public static final int IT_REMOTECONSOLENGINX_INGRESS_HTTPS_HOSTPORT = 2228; + public static final int IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT = 31132; + public static final int IT_ONPREMCRDOMAINTX_INGRESS_HTTP_HOSTPORT = 8001; + } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java index 0b561b37194..2e50228c211 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ImageUtils.java @@ -209,14 +209,15 @@ public static String createDiiImageAndVerify(String domainUid, } /** - * Create an image with modelfile, application archive and property file. If the property file - * is needed to be updated with a property that has been created by the framework, it is copied - * onto RESULT_ROOT and updated. Hence the altModelDir. Call this method to create a domain home in image. + * Create an image with modelfile, application archive and property file.If the property file + is needed to be updated with a property that has been created by the framework, it is copied + onto RESULT_ROOT and updated. Hence the altModelDir. Call this method to create a domain home in image. * @param imageNameBase - base image name used in local or to construct image name in repository * @param wdtModelList - model file used to build the image * @param appSrcDirList - application to be added to the image * @param modelPropFile - property file to be used with the model file above * @param altModelDir - directory where the property file is found if not in the default MODEL_DIR + * @param domainUid domain id * @return image name with tag */ public static String createImageAndVerify(String imageNameBase, @@ -224,13 +225,13 @@ public static String createImageAndVerify(String imageNameBase, List appSrcDirList, String modelPropFile, String altModelDir, - String domainHome) { + String domainUid) { final List modelPropList = Collections.singletonList(altModelDir + "/" + modelPropFile); return createImageAndVerify( imageNameBase, wdtModelList, appSrcDirList, modelPropList, WEBLOGIC_IMAGE_NAME, - WEBLOGIC_IMAGE_TAG, WLS, false, domainHome, false); + WEBLOGIC_IMAGE_TAG, WLS, false, domainUid, false); } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java index 13bfc118e0d..ccee85686d5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/OracleHttpClient.java @@ -4,11 +4,15 @@ package oracle.weblogic.kubernetes.utils; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ProxySelector; import java.net.Socket; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.net.http.HttpTimeoutException; +import java.nio.file.Paths; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -16,6 +20,7 @@ import java.util.Base64; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; @@ -126,6 +131,64 @@ public static HttpResponse get(String url, Map headers) return get(url, headers, false); } + /** + * Http GET request to download a file and save it in the give destination directory. + * + * @param url URL of the file to download + * @param destLocation detination directory where to save the downloaded file + * @param proxyHost optional proxy host, can be null for no proxy + * @param proxyPort optional proxy port, can be null for no proxy + * @param maxRetries the maximum number of retries before it can return false + * @return true if download succeeds otherwide false + */ + public static boolean downloadFile(String url, String destLocation, String proxyHost, + String proxyPort, int maxRetries) { + LoggingFacade logger = getLogger(); + + // Build HttpClient with optional proxy and retry policy + HttpClient.Builder clientBuilder = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .followRedirects(HttpClient.Redirect.NORMAL) + .connectTimeout(Duration.ofSeconds(10)); + + // Configure proxy if PROXY_HOST and PROXY_PORT are specified + if (proxyHost != null && proxyPort != null) { + clientBuilder.proxy(ProxySelector.of(new InetSocketAddress(proxyHost, Integer.valueOf(proxyPort)))); + logger.info("Proxy configured: {0}:{1}", proxyHost, proxyPort); + } else { + logger.info("No proxy configuration provided."); + } + + HttpClient client = clientBuilder.build(); + + // Prepare the HttpRequest + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .timeout(Duration.ofMinutes(2)) + .GET() + .build(); + + // Attempt to download the file + for (int attempt = 1; attempt <= maxRetries; attempt++) { + try { + logger.info("Starting download..."); + if (client.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get(destLocation))).statusCode() != 200) { + logger.info("Failed download retrying..."); + TimeUnit.SECONDS.sleep(10); + continue; + } else { + logger.info("Download completed successfully."); + return true; + } + } catch (HttpTimeoutException e) { + logger.severe("Request timed out: {0}", e.getMessage()); + } catch (IOException | InterruptedException e) { + logger.severe("Download failed: {0}", e.getMessage()); + } + } + return false; + } + private static final TrustManager MOCK_TRUST_MANAGER = new X509ExtendedTrustManager() { @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { diff --git a/integration-tests/src/test/resources/bash-scripts/install-wls.sh b/integration-tests/src/test/resources/bash-scripts/install-wls.sh new file mode 100644 index 00000000000..baf777de54e --- /dev/null +++ b/integration-tests/src/test/resources/bash-scripts/install-wls.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +echo $JAVA_HOME +if [ -n "$1" ] || [ -n "$result_root" ]; then + echo "RESULT_ROOT or result_root is set" +else + echo "no RESULT_ROOT or result_root is set, exiting wls installation." + exit 0 +fi + +if [ -n "$2" ] || [ -n "$SHIPHOME_DOWNLOAD_SERVER" ]; then + echo "SHIPHOME_DOWNLOAD_SERVER is set" + else + echo "no SHIPHOME_DOWNLOAD_SERVER is set, exiting wls installation." + exit 0 + fi + +if [ -z "$1" ]; then + echo "running in Jenkins, result_root is set" +else + echo "running in localhost, RESULT_ROOT is set : $1" + result_root=$1 + SHIPHOME_DOWNLOAD_SERVER=$2 + +fi +echo $result_root +echo $SHIPHOME_DOWNLOAD_SERVER +MW_HOME="$result_root/mwhome" +SILENT_RESPONSE_FILE=$result_root/silent.response +ORAINVENTORYPOINTER_LOC=$result_root/oraInv.loc +ORAINVENTORY_LOC=$result_root/oraInventory +WLS_SHIPHOME=$result_root/fmw_wls_generic.jar +DOWNLOAD_URL="http://$SHIPHOME_DOWNLOAD_SERVER/results/release/src141200/fmw_14.1.2.0.0_wls_generic.jar" +SUCCESS="The\ installation\ of\ Oracle\ Fusion\ Middleware.*completed\ successfully" + +rm -rf $MW_HOME/* +rm -rf $SILENT_RESPONSE_FILE +rm -rf $ORAINVENTORY_LOC/* +rm -rf $ORAINVENTORYPOINTER_LOC +mkdir -p $MW_HOME +mkdir -p $ORAINVENTORY_LOC + +echo "creating $SILENT_RESPONSE_FILE file with contents" + +cat < $SILENT_RESPONSE_FILE +[ENGINE] +Response File Version=1.0.0.0.0 +[GENERIC] +ORACLE_HOME=$MW_HOME +INSTALL_TYPE=WebLogic Server +EOF + +cat $SILENT_RESPONSE_FILE + +echo "creating $ORAINVENTORYPOINTER_LOC file with contents" + +cat < $ORAINVENTORYPOINTER_LOC +inventory_loc=$ORAINVENTORY_LOC +inst_group=opc +EOF + +cat $ORAINVENTORYPOINTER_LOC + +#download WebLogic shiphome installer +curl -Lo $WLS_SHIPHOME $DOWNLOAD_URL +ls -l $WLS_SHIPHOME +md5sum $WLS_SHIPHOME + +#install WebLogic +echo "Running java -jar $WLS_SHIPHOME -silent -responseFile $SILENT_RESPONSE_FILE -invPtrLoc $ORAINVENTORYPOINTER_LOC" +install_log=$(java -jar $WLS_SHIPHOME -silent -responseFile $SILENT_RESPONSE_FILE -invPtrLoc $ORAINVENTORYPOINTER_LOC) +if [[ "$install_log" =~ $SUCCESS ]]; then + echo "The installation of WebLogic completed successfully." + . $MW_HOME/wlserver/server/bin/setWLSEnv.sh + java weblogic.version +else + echo "The installation of WebLogic failed." +fi diff --git a/integration-tests/src/test/resources/onpremcrtx/model-cdt-jms.yaml b/integration-tests/src/test/resources/onpremcrtx/model-cdt-jms.yaml new file mode 100644 index 00000000000..df8fb6c2600 --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model-cdt-jms.yaml @@ -0,0 +1,49 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +resources: + FileStore: + TestCdtFileStore: + Target: 'cluster-1' + JMSServer: + TestCdtJmsServer: + ProductionPausedAtStartup: false + ConsumptionPausedAtStartup: false + Target: 'cluster-1' + PersistentStore: 'TestCdtFileStore' + InsertionPausedAtStartup: false + MessageCompressionOptions: GZIP_DEFAULT_COMPRESSION + TestAdminJmsServer: + ProductionPausedAtStartup: false + ConsumptionPausedAtStartup: false + Target: 'admin-server' + InsertionPausedAtStartup: false + MessageCompressionOptions: GZIP_DEFAULT_COMPRESSION + + JMSSystemResource: + TestAdminJmsModule: + Target: 'admin-server' + SubDeployment: + TestAdminSubDeployment: + Target: TestAdminJmsServer + JmsResource: + Queue: + testAccountingQueue: + SubDeploymentName: TestAdminSubDeployment + JNDIName: jms.testAccountingQueue + TestCdtJmsModule: + Target: 'cluster-1' + SubDeployment: + TestCdtSubDeployment: + Target: TestCdtJmsServer + JmsResource: + UniformDistributedQueue: + testCdtUniformQueue: + SubDeploymentName: TestCdtSubDeployment + JNDIName: jms/testCdtUniformQueue + ConnectionFactory: + ClusterConnectionFactory: + JNDIName: jms.ClusterConnectionFactory + DefaultTargetingEnabled: true + LoadBalancingParams: + ServerAffinityEnabled: false + LoadBalancingEnabled: true diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties new file mode 100644 index 00000000000..758dccc32fa --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties @@ -0,0 +1,7 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +DOMAIN_NAME=domain1 +ADMIN_USERNAME=weblogic +ADMIN_PASSWORD=welcome1 + diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml new file mode 100644 index 00000000000..f8dd69a5831 --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@PROP:ADMIN_USERNAME@@' + AdminPassword: '@@PROP:ADMIN_PASSWORD@@' + ServerStartMode: 'prod' + +topology: + Name: '@@PROP:DOMAIN_NAME@@' + AdminServerName: "admin-server" + Cluster: + "cluster-1": + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "managed-server" + DynamicClusterSize: 2 + MaxDynamicClusterSize: 2 + CalculatedListenPorts: false + Server: + "admin-server": + ListenPort: 7001 + ExternalDNSName: '@@PROP:DOMAIN_NAME@@-admin-server.@@PROP:NAMESPACE@@' + + ServerTemplate: + "cluster-1-template": + Cluster: "cluster-1" + ListenPort : 8001 + ExternalDNSName: '@@PROP:DOMAIN_NAME@@-managed-server${id}.@@PROP:NAMESPACE@@' + DataSource: + RmiJDBCSecurity: Compatibility + +appDeployments: + Application: + jmservlet: + SourcePath: "wlsdeploy/applications/jmsservlet.war" + ModuleType: war + Target: 'admin-server' + mdbtopic: + SourcePath: "wlsdeploy/applications/mdbtopic.jar" + ModuleType: jar + Target: 'cluster-1' diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.properties b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.properties new file mode 100644 index 00000000000..a68a56bf292 --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.properties @@ -0,0 +1,6 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +DOMAIN_NAME=domain2 +ADMIN_USERNAME=weblogic +ADMIN_PASSWORD=welcome1 diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.yaml b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.yaml new file mode 100644 index 00000000000..67a878a7d8c --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain2.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@PROP:ADMIN_USERNAME@@' + AdminPassword: '@@PROP:ADMIN_PASSWORD@@' + ServerStartMode: 'prod' + +topology: + Name: '@@PROP:DOMAIN_NAME@@' + AdminServerName: "admin-server" + Cluster: + "cluster-1": + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "managed-server" + DynamicClusterSize: 2 + MaxDynamicClusterSize: 2 + CalculatedListenPorts: true + Server: + "admin-server": + ListenPort: 7001 + ExternalDNSName: '@@PROP:DNS_NAME@@' + ServerTemplate: + "cluster-1-template": + Cluster: "cluster-1" + ListenPort : 8001 + ExternalDNSName: '@@PROP:DNS_NAME@@' + DataSource: + RmiJDBCSecurity: Compatibility diff --git a/integration-tests/src/test/resources/onpremcrtx/model2-cdt-jms.yaml b/integration-tests/src/test/resources/onpremcrtx/model2-cdt-jms.yaml new file mode 100644 index 00000000000..460e1475e43 --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model2-cdt-jms.yaml @@ -0,0 +1,34 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +resources: + FileStore: + TestCdtFileStore: + Target: 'cluster-1' + JMSServer: + TestCdtJmsServer: + ProductionPausedAtStartup: false + ConsumptionPausedAtStartup: false + Target: 'cluster-1' + PersistentStore: 'TestCdtFileStore' + InsertionPausedAtStartup: false + MessageCompressionOptions: GZIP_DEFAULT_COMPRESSION + + JMSSystemResource: + TestCdtJmsModule: + Target: 'cluster-1' + SubDeployment: + TestCdtSubDeployment: + Target: TestCdtJmsServer + JmsResource: + UniformDistributedTopic: + testCdtUniformTopic: + SubDeploymentName: TestCdtSubDeployment + JNDIName: jms.testCdtUniformTopic + ForwardingPolicy: Partitioned + ConnectionFactory: + ClusterConnectionFactory: + JNDIName: jms.ClusterConnectionFactory + DefaultTargetingEnabled: true + LoadBalancingParams: + ServerAffinityEnabled: false + LoadBalancingEnabled: true diff --git a/integration-tests/src/test/resources/onpremcrtx/onprem-domain-routing.yaml b/integration-tests/src/test/resources/onpremcrtx/onprem-domain-routing.yaml new file mode 100644 index 00000000000..971326f3ffb --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/onprem-domain-routing.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: path-routing-ingress + namespace: NAMESPACE +spec: + ingressClassName: traefik-onprem + rules: + - host: domain1-managed-server1.NAMESPACE + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: domain1-managed-server1 + port: + number: 8001 + - host: domain1-managed-server2.NAMESPACE + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: domain1-managed-server2 + port: + number: 8001 \ No newline at end of file diff --git a/integration-tests/src/test/resources/onpremcrtx/shutdown.py b/integration-tests/src/test/resources/onpremcrtx/shutdown.py new file mode 100644 index 00000000000..29096f3c8dc --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/shutdown.py @@ -0,0 +1,11 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +hostname=sys.argv[1] +connect('weblogic','welcome1','t3://'+hostname+':7001') +shutdown('managed-server1', 'Server', ignoreSessions='true', force='true') +shutdown('managed-server2', 'Server', ignoreSessions='true', force='true') +shutdown('admin-server', 'Server', ignoreSessions='true', force='true') +disconnect() +exit() + From d2575c217ca824eeffd7bd5f6afd52ecfe7ec3b8 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Wed, 20 Nov 2024 14:36:16 +0000 Subject: [PATCH 231/356] increase timeout value in ELK tests and fix OCNE test failures --- Jenkinsfile.ocne19 | 12 ++++++------ .../kubernetes/actions/impl/LoggingExporter.java | 3 +++ .../kubernetes/utils/CommonMiiTestUtils.java | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile.ocne19 b/Jenkinsfile.ocne19 index f69bc6da3a7..249b6ae44eb 100644 --- a/Jenkinsfile.ocne19 +++ b/Jenkinsfile.ocne19 @@ -322,7 +322,7 @@ fingerprint=${FINGERPRINT} api_private_key_path=${jenkins_home_directory}/.oci/oci-signing-key.pem region=${wko_region} availability_domain_id=${availability_domain} -prefix=ocne-tf-jenkin-${BUILD_ID} +prefix=ocne-tf-jenkin42-${BUILD_ID} deploy_networking=false subnet_id=${private_subnet_ocid} @@ -424,7 +424,7 @@ EOF stage('Preparing Integration Test Environment - mount PV root in OCNE cluster control node') { steps { sh ''' - prefix=ocne-tf-jenkin-${BUILD_ID} + prefix=ocne-tf-jenkin42-${BUILD_ID} # Get the OCNE cluster control node private IP k8s_master_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-control-plane-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` k8s_master_node_ip=`oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_master_instance_id} |jq -r '.data[] | select(."hostname-label" != null) | ."private-ip"'` @@ -440,7 +440,7 @@ EOF stage('Preparing Integration Test Environment - mount PV root in OCNE cluster worker1 node') { steps { sh ''' - prefix=ocne-tf-jenkin-${BUILD_ID} + prefix=ocne-tf-jenkin42-${BUILD_ID} # Get the OCNE cluster worker1 node private IP k8s_worker1_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-worker-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` k8s_worker1_node_ip=`oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_worker1_instance_id} |jq -r '.data[] | select(."hostname-label" != null) | ."private-ip"'` @@ -456,7 +456,7 @@ EOF stage('Preparing Integration Test Environment - mount PV root in OCNE cluster worker2 node') { steps { sh ''' - prefix=ocne-tf-jenkin-${BUILD_ID} + prefix=ocne-tf-jenkin42-${BUILD_ID} # Get the OCNE cluster worker2 node private IP k8s_worker2_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-worker-002 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` k8s_worker2_node_ip=`oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_worker2_instance_id} |jq -r '.data[] | select(."hostname-label" != null) | ."private-ip"'` @@ -508,7 +508,7 @@ EOF mkdir -m777 -p "${WORKSPACE}/.mvn" touch ${WORKSPACE}/.mvn/maven.config export KUBECONFIG=${kubeconfig_file} - prefix=ocne-tf-jenkin-${BUILD_ID} + prefix=ocne-tf-jenkin42-${BUILD_ID} # Get the OCNE cluster control node private IP k8s_master_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-control-plane-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` @@ -567,7 +567,7 @@ EOF ]) { sh ''' - prefix=ocne-tf-jenkin-${BUILD_ID} + prefix=ocne-tf-jenkin42-${BUILD_ID} k8s_master_instance_id=`oci compute instance list --compartment-id=${compartment_id} --display-name=${prefix}-control-plane-001 |jq -r '.data[] | select(."lifecycle-state" == "RUNNING") | ."id"'` k8s_master_instance_private_ip=`oci compute instance list-vnics --compartment-id=${compartment_id} --instance-id=${k8s_master_instance_id} |jq -r '.data[] | select(."hostname-label" == null) | ."private-ip"'` diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java index 0073d1ebbda..7e479b84952 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/LoggingExporter.java @@ -46,6 +46,7 @@ import static oracle.weblogic.kubernetes.assertions.impl.Kubernetes.isPodReady; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -103,6 +104,7 @@ public static boolean installElasticsearch(LoggingExporterParams params) { logger.info("Check if Elasticsearch deployment {0} is ready in namespace {1}", elasticsearchName, namespace); testUntil( + withLongRetryPolicy, Deployment.isReady(elasticsearchName, labels, namespace), logger, "Elasticsearch deployment {0} to be completed in {1} namespace", @@ -170,6 +172,7 @@ public static boolean installKibana(LoggingExporterParams params) { logger.info("Checking if Kibana deployment is ready {0} completed in namespace {1}", kibanaName, namespace); testUntil( + withLongRetryPolicy, Deployment.isReady(kibanaName, labels, namespace), logger, "Kibana deployment {0} to be completed in namespace {1}", diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java index 184b75bc42c..b03d99832a7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonMiiTestUtils.java @@ -1116,7 +1116,7 @@ public static String readRuntimeResource(String adminSvcExtHost, String domainNa } } else { String curlString; - if (OKE_CLUSTER_PRIVATEIP) { + if (OKE_CLUSTER_PRIVATEIP || OCNE) { String protocol = "http"; String port = "7001"; From f2cc5712133b1327a85b9186cfb719f6a207d6d8 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 20 Nov 2024 10:03:35 -0500 Subject: [PATCH 232/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 84a1172cd57..7663310eb37 100644 --- a/pom.xml +++ b/pom.xml @@ -713,7 +713,7 @@ 3.6.0 1.0.0 3.26.3 - 2.17.0 + 2.18.0 4.2.2 19.0.1 3.0.1u2 @@ -740,7 +740,7 @@ 1.5.12 4.28.3 2.5.1 - 9.46 + 9.47 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 204c930fbf672436e6b32a443b287462217ea99b Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 20 Nov 2024 16:40:43 -0500 Subject: [PATCH 233/356] Prepare for 4.2.12 release --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 2e1dfbf47a7..20f5ee82abf 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 491aa66ff6a..11929cb26e0 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 0b3212ac5e2..145795c5fe1 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index b03290500a6..57209610d20 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.12-SNAPSHOT + 4.2.12 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 0da8084ba32..10e115df2bc 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 8a27b75a42c..13038def659 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.12-SNAPSHOT + 4.2.12 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 0f72cab76e3..1cfda2d8615 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 7663310eb37..fed619f1e8d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index eafe6877497..fd6ea6cd783 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12-SNAPSHOT + 4.2.12 operator-swagger From 02bf92a2ced7c41c3f2c531a7c18e011e88aeef0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 20 Nov 2024 17:03:36 -0500 Subject: [PATCH 234/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 20f5ee82abf..87b62effde2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 11929cb26e0..5de2af399ed 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 145795c5fe1..cd6b8c7c840 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 57209610d20..354539fd045 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.12 + 4.2.13-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 10e115df2bc..771586fe99b 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 13038def659..3e4805c11ff 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.12 + 4.2.13-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 1cfda2d8615..4b067d802f2 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index fed619f1e8d..b19eab18c04 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index fd6ea6cd783..012e68c3f17 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.12 + 4.2.13-SNAPSHOT operator-swagger From c4184d5532fe9b2336b8f3cd55792e70a9f80130 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Wed, 27 Nov 2024 16:22:01 +0000 Subject: [PATCH 235/356] Remove the verrazano test profile --- integration-tests/pom.xml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index cd6b8c7c840..a6bf50f8471 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -765,14 +765,7 @@ toolkits-srg - - v8o - - false - v8o - - - + integration-test-env From a0aa12454cc6f1ff76a1638780d5974ab4ce174b Mon Sep 17 00:00:00 2001 From: xian_cao Date: Wed, 27 Nov 2024 18:05:41 +0000 Subject: [PATCH 236/356] add retry when checking server log in ItPodsShutdownOption and fix ItFmwDomainInPVUsingWLST in OCNE --- Jenkinsfile.ocne19 | 2 +- .../kubernetes/ItFmwDomainInPVUsingWLST.java | 3 ++- .../kubernetes/ItPodsShutdownOption.java | 23 +++++++++++++------ .../weblogic/kubernetes/utils/JobUtils.java | 2 ++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Jenkinsfile.ocne19 b/Jenkinsfile.ocne19 index 249b6ae44eb..47168819138 100644 --- a/Jenkinsfile.ocne19 +++ b/Jenkinsfile.ocne19 @@ -72,7 +72,7 @@ pipeline { ) choice(name: 'MAVEN_PROFILE_NAME', - description: 'Profile to use in mvn command to run the tests. Possible values are olcne (the default), wko-olcne-cert and integration-tests. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + description: 'Profile to use in mvn command to run the tests. Possible values are olcne-srg (the default), olcne-mrg, olcne-sequential and integration-tests. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', choices: [ 'olcne-srg', 'olcne-mrg', diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java index ea0c8654e7e..5bd10294e81 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPVUsingWLST.java @@ -41,6 +41,7 @@ import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.OCNE; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; @@ -128,7 +129,7 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace is null"); jrfDomainNamespace = namespaces.get(2); - if (OKD) { + if (OKD || OCNE) { logger.info("Start DB and create RCU schema for namespace: {0}, dbListenerPort: {1}, RCU prefix: {2}, " + "dbUrl: {3}, dbImage: {4}, fmwImage: {5} ", dbNamespace, dbListenerPort, RCUSCHEMAPREFIX, dbUrl, DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java index 62985abfc66..39a7f032898 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodsShutdownOption.java @@ -54,6 +54,7 @@ import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterAndVerify; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResource; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.shutdownDomainAndVerify; import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; @@ -544,12 +545,21 @@ private void createVerifyDomain(DomainResource domain) { // get pod log which includes the server.out logs and verify the messages contain the set shutdown properties private void verifyServerLog(String namespace, String podName, String[] envVars) { - String podLog = assertDoesNotThrow(() -> TestActions.getPodLog(podName, namespace)); - for (String envVar : envVars) { - logger.info("Checking Pod {0} for server startup property {1}", podName, envVar); - assertTrue(podLog.contains(envVar), "Server log doesn't contain the " + envVar); - logger.info("Pod {0} contains the property {1} in server startup env", podName, envVar); - } + testUntil( + () -> { + boolean result = true; + String podLog = assertDoesNotThrow(() -> TestActions.getPodLog(podName, namespace)); + for (String envVar : envVars) { + logger.info("Checking Pod {0} for server startup property {1}", podName, envVar); + result = result && podLog.contains(envVar); + } + return result; + }, + logger, + "server log for pod {0} contains environment variables {1}", + podName, + envVars + ); } // Crate a ConfigMap with a model to add a 2 independent managed servers @@ -570,6 +580,5 @@ private static void createModelConfigMap(String configMapName, String model) { String.format("Can't create ConfigMap %s", configMapName)); assertTrue(cmCreated, String.format("createConfigMap failed %s", configMapName)); } - } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java index c5f28e78747..b830e6fc85c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/JobUtils.java @@ -33,6 +33,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.listPods; import static oracle.weblogic.kubernetes.assertions.TestAssertions.jobCompleted; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createfixPVCOwnerContainer; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -156,6 +157,7 @@ public static void createDomainJob(String image, String pvName, String pvcName, logger.info("Checking if the domain creation job {0} completed in namespace {1}", jobName, namespace); testUntil( + withLongRetryPolicy, jobCompleted(jobName, null, namespace), logger, "job {0} to be completed in namespace {1}", From 100fa1bf65ae5069bcdf7d1e6e819f0eb55bf113 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 2 Dec 2024 12:58:34 +0000 Subject: [PATCH 237/356] Merge branch 'update-aks-doc-20241106' into 'main' On branch update-aks-doc-20241106 Copyediting. See merge request weblogic-cloud/weblogic-kubernetes-operator!4885 (cherry picked from commit 3353c539226198ee5f7cbe4a4644cea563c5b9aa) ec0462b4 update aks userguide for Tags 7c1b1104 use built-in content table in AKS samples. 96e5ca6d On branch update-aks-doc-20241106 Copyediting. --- .../content/managing-domains/aks/_index.md | 18 +++- .../azure-kubernetes-service/domain-on-pv.md | 87 +++++++----------- .../includes/create-aks-cluster-body-01.txt | 2 - .../includes/create-aks-cluster-body-02.txt | 3 - .../includes/create-aks-cluster-storage.txt | 8 +- .../includes/create-resource-group.txt | 3 - .../includes/download-samples-zip.txt | 2 - .../includes/prerequisites-01.txt | 2 - .../includes/prerequisites-02.txt | 2 - .../includes/sign-in-azure.txt | 2 - .../model-in-image.md | 84 ++++++++--------- .../site/static/images/aks-solution.png | Bin 88692 -> 95995 bytes 12 files changed, 90 insertions(+), 123 deletions(-) diff --git a/documentation/site/content/managing-domains/aks/_index.md b/documentation/site/content/managing-domains/aks/_index.md index c0ad0934c7b..f8684074730 100644 --- a/documentation/site/content/managing-domains/aks/_index.md +++ b/documentation/site/content/managing-domains/aks/_index.md @@ -75,9 +75,10 @@ In this section, you can configure some options about the AKS which will run Web | Field | Description | |-------|-------------| |Create a new AKS cluster| If set to **Yes**, the deployment will create a new AKS cluster resource in the specified resource group. If set to **No**, you have the opportunity to select an existing AKS cluster, into which the deployment is configured. Note: the offer assumes the existing AKS cluster has no WebLogic related deployments. | +| Node size | The default VM size is 2x Standard DSv2, 2 vcpus, 7 GB memory. If you want to select a different VM size, select **Change Size**, select the size from the list (for example, A3) on the Select a VM size page, and select **Select**. For more information about sizing the virtual machine, see the [Azure documentation on Sizes](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/overview).| | Minimum node count | The minimum node count in the AKS cluster. This value can be changed after deployment. For information, see [AKS autoscaler](https://learn.microsoft.com/azure/aks/cluster-autoscaler). | | Maximum node count | The maximum node count in the AKS cluster. This value can be changed after deployment. For information, see [AKS autoscaler](https://learn.microsoft.com/azure/aks/cluster-autoscaler). | -| Node size | The default VM size is 2x Standard DSv2, 2 vcpus, 7 GB memory. If you want to select a different VM size, select **Change Size**, select the size from the list (for example, A3) on the Select a VM size page, and select **Select**. For more information about sizing the virtual machine, see the [Azure documentation on Sizes](https://docs.microsoft.com/azure/cloud-services/cloud-services-sizes-specs).| +| Select AKS cluster | This option is shown if **Create a new AKS cluster?** is set to **No**. If visible, select an existing Azure Kubernetes Service instance. | #### Image selection @@ -86,12 +87,13 @@ In this section, you can configure the image that is deployed using the model-in | Field | Description | |-------|-------------| |Create a new Azure Container Registry to store application images?|If set to **Yes**, the offer will create a new Azure Container Registry (ACR) to hold the images for use in the deployment. If set to **No**, you must specify an existing ACR. In this case, you must be sure the selected ACR has the admin account enabled. For details, please see [Admin account](https://docs.microsoft.com/azure/container-registry/container-registry-authentication?tabs=azure-cli#admin-account). | -| Select ACR instance | This option is shown if **Create a new Azure Container Registry to store application images?** is set to **No**. If visible, select an existing Acure Container Registry instance. | +| Select ACR instance | This option is shown if **Create a new Azure Container Registry to store application images?** is set to **No**. If visible, select an existing Azure Container Registry instance. | | Username for Oracle Single Sign-On authentication | The Oracle Single Sign-on account user name for which the Terms and Restrictions for the selected WebLogic Server image have been accepted. | | Password for Oracle Single Sign-On authentication | The password for that account. | | Confirm password | Re-enter the value of the preceding field. | If 'Yes' is selected; the deployment process will pull from the CPU WebLogic Server image repository in the OCR. If 'No' is selected the deployment process will pull from the WebLogic Server image repository in OCR. | | Select the type of WebLogic Server Images. | If set to **Patched WebLogic Server Images**, you must accept the license agreement in the `middleware/weblogic_cpu` repository. If set to **General WebLogic Server Images**, you must accept the license agreement in the `middleware/weblogic`. Steps to accept the license agreement: log in to the [Oracle Container Registry](https://container-registry.oracle.com/); navigate to the `middleware/weblogic_cpu` and `middleware/weblogic` repository; accept license agreement. See this [document](https://aka.ms/wls-aks-ocr-doc) for more information. | | Select desired combination of WebLogic Server, JDK and Operating System or fully qualified Docker tag | Select one of the supported images. | +| WebLogic Docker tag | This option is shown if **Select desired combination of WebLogic Server, JDK and Operating System or fully qualified Docker tag** is set to **Others**. If visible, input a fully qualified Docker tag. You can find the available tags from WebLogic Server Images page of [Oracle Container Registry](https://container-registry.oracle.com/) | #### Application @@ -333,6 +335,18 @@ After the provisioning is complete, you can create KEDA scaling rules. A sample * The **shellCmdtoOutputKedaScalerSample** value is the base64 string of a scaler sample. Copy the value and run it in your terminal. * For guidance on how to complete the configuration, see [Tutorial: Migrate Oracle WebLogic Server to AKS with KEDA scaler based on Prometheus Metrics](https://aka.ms/wls-aks-keda-scaler). +When you are satisfied with your selections, select **Next** and open the **Tags** blade. + +### Tags + +Use the Tags blade to provide tags for resources and resource groups. Tags are name/value pairs that allow you to categorize resources and consolidate billing by applying the same tag to multiple resources and resource groups. [Learn more about tags](https://go.microsoft.com/fwlink/?linkid=873112). + +Enter the desired name/value pairs for resources in the table, filling out each of the following fields. + +| Name | Value | Resource | +|------|-------|----------| +| Tag name. | Tag value. | Resources available for tagging in this offer. | + When you are satisfied with your selections, select **Review + create**. ### Review + create diff --git a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md index 51c3fa50634..87634ab650b 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md +++ b/documentation/site/content/samples/azure-kubernetes-service/domain-on-pv.md @@ -7,30 +7,13 @@ description: "Sample for creating a WebLogic domain home on an existing PV or PV This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://oracle.github.io/weblogic-kubernetes-operator) (hereafter "the operator") to set up a WebLogic Server (WLS) cluster on the Azure Kubernetes Service (AKS) using the domain on PV approach. After going through the steps, your WLS domain runs on an AKS cluster and you can manage your WLS domain by accessing the WebLogic Server Administration Console. -#### Contents - - - [Prerequisites](#prerequisites) - - [Create Resource Group](#create-resource-group) - - [Create an AKS cluster](#create-the-aks-cluster) - - [Create and configure storage](#create-storage) - - [Create an Azure Storage account and NFS share](#create-an-azure-storage-account-and-nfs-share) - - [Create SC and PVC](#create-sc-and-pvc) - - [Create a domain creation image](#create-a-domain-creation-image) - - [Install WebLogic Kubernetes Operator](#install-weblogic-kubernetes-operator-into-the-aks-cluster) - - [Create WebLogic domain](#create-weblogic-domain) - - [Create secrets](#create-secrets) - - [Create WebLogic Domain](#create-weblogic-domain-1) - - [Create LoadBalancer](#create-loadbalancer) - - [Automation](#automation) - - [Access sample application](#access-sample-application) - - [Validate NFS volume](#validate-nfs-volume) - - [Clean up resources](#clean-up-resources) - - [Troubleshooting](#troubleshooting) - - [Useful links](#useful-links) +{{< table_of_contents >}} + +### Prerequisites {{< readfile file="/samples/azure-kubernetes-service/includes/prerequisites-01.txt" >}} -##### Prepare parameters +#### Prepare parameters Set required parameters by running the following commands. @@ -57,36 +40,40 @@ export ACR_NAME="${NAME_PREFIX}acr${TIMESTAMP}" ``` +#### Oracle Container Registry + {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt" >}} +#### Sign in with Azure CLI + {{< readfile file="/samples/azure-kubernetes-service/includes/sign-in-azure.txt" >}} +#### Download the WebLogic Kubernetes Operator sample + {{< readfile file="/samples/azure-kubernetes-service/includes/download-samples-zip.txt" >}} {{% notice info %}} The following sections of the sample instructions will guide you, step-by-step, through the process of setting up a WebLogic cluster on AKS - remaining as close as possible to a native Kubernetes experience. This lets you understand and customize each step. If you wish to have a more automated experience that abstracts some lower level details, you can skip to the [Automation](#automation) section. {{% /notice %}} +### Create Resource Group + {{< readfile file="/samples/azure-kubernetes-service/includes/create-resource-group.txt" >}} +### Create the AKS cluster + {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt" >}} **NOTE**: If you run into VM size failure, see [Troubleshooting - Virtual Machine size is not supported]({{< relref "/samples/azure-kubernetes-service/troubleshooting#virtual-machine-size-is-not-supported" >}}). + ### Create storage + {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt" >}} -#### Create a domain creation image +### Create a domain creation image This sample requires [Domain creation images]({{< relref "/managing-domains/domain-on-pv/domain-creation-images" >}}). For more information, see [Domain on Persistent Volume]({{< relref "/managing-domains/domain-on-pv/overview" >}}). - - [Image creation prerequisites](#image-creation-prerequisites) - - [Image creation - Introduction](#image-creation---introduction) - - [Understanding your first archive](#understanding-your-first-archive) - - [Staging a ZIP file of the archive](#staging-a-zip-file-of-the-archive) - - [Staging model files](#staging-model-files) - - [Creating the image with WIT](#creating-the-image-with-wit) - - [Pushing the image to Azure Container Registry](#pushing-the-image-to-azure-container-registry) - -##### Image creation prerequisites +#### Image creation prerequisites - The `JAVA_HOME` environment variable must be set and must reference a valid JDK 8 or 11 installation. - Copy the sample to a new directory; for example, use the directory `/tmp/dpv-sample`. In the directory name, `dpv` is short for "domain on pv". Domain on PV is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). @@ -113,15 +100,15 @@ This sample requires [Domain creation images]({{< relref "/managing-domains/doma {{< readfile file="/samples/azure-kubernetes-service/includes/download-wls-tools.txt" >}} -##### Image creation - Introduction +#### Image creation - Introduction {{< readfile file="/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt" >}} -##### Understanding your first archive +#### Understanding your first archive See [Understanding your first archive]({{< relref "/samples/domains/domain-home-on-pv/build-domain-creation-image#understand-your-first-archive" >}}). -##### Staging a ZIP file of the archive +#### Staging a ZIP file of the archive Delete any possible existing archive.zip in case we have an old leftover version. @@ -136,13 +123,13 @@ $ cd /tmp/dpv-sample/archives/archive-v1 $ zip -r ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip wlsdeploy ``` -##### Staging model files +#### Staging model files {{< readfile file="/samples/azure-kubernetes-service/includes/staging-model-files.txt" >}} An image can contain multiple properties files, archive ZIP files, and model YAML files but in this sample you use just one of each. For a complete description of WDT model file naming conventions, file loading order, and macro syntax, see [Model files]({{< relref "/managing-domains/domain-on-pv/model-files" >}}) in the user documentation. -##### Creating the image with WIT +#### Creating the image with WIT {{< readfile file="/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt" >}} @@ -154,7 +141,7 @@ The `imagetool.sh` is not supported on macOS with Apple Silicon. See [Troublesho You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled, see [Troubleshooting - WebLogic Image Tool failure]({{< relref "/samples/azure-kubernetes-service/troubleshooting#weblogic-image-tool-failure" >}}). {{% /notice %}} -##### Pushing the image to Azure Container Registry +#### Pushing the image to Azure Container Registry {{< readfile file="/samples/azure-kubernetes-service/includes/create-acr.txt" >}} @@ -169,7 +156,7 @@ $ docker push ${LOGIN_SERVER}/wdt-domain-image:WLS-v1 If you see an error that seems related to you not being an **Owner on this subscription**, please refer to the troubleshooting section [Cannot attach ACR due to not being Owner of subscription]({{< relref "/samples/azure-kubernetes-service/troubleshooting#cannot-attach-acr-due-to-not-being-owner-of-subscription" >}}). -#### Install WebLogic Kubernetes Operator into the AKS cluster +### Install WebLogic Kubernetes Operator into the AKS cluster The WebLogic Kubernetes Operator is an adapter to integrate WebLogic Server and Kubernetes, allowing Kubernetes to serve as a container infrastructure hosting WLS instances. The operator runs as a Kubernetes Pod and stands ready to perform actions related to running WLS on Kubernetes. @@ -207,15 +194,11 @@ weblogic-operator-webhook-868db5875b-55v7r 1/1 Running 0 86s {{% notice tip %}} You will have to press Ctrl-C to exit this command due to the `-w` flag. {{% /notice %}} -#### Create WebLogic domain - - - [Create secrets](#create-secrets) - - [Create WebLogic Domain](#create-weblogic-domain-1) - - [Create LoadBalancer](#create-loadbalancer) +### Create WebLogic domain Now that you have created the AKS cluster, installed the operator, and verified that the operator is ready to go, you can ask the operator to create a WLS domain. -##### Create secrets +#### Create secrets You will use the `$BASE_DIR/sample-scripts/create-weblogic-domain-credentials/create-weblogic-credentials.sh` script to create the domain WebLogic administrator credentials as a Kubernetes secret. Please run the following commands: @@ -267,7 +250,7 @@ wlsregcred kubernetes.io/dockerconfigjson 1 **NOTE**: If the `NAME` column in your output is missing any of the values shown above, please review your execution of the preceding steps in this sample to ensure that you correctly followed all of them. -##### Enable Weblogic Operator +#### Enable Weblogic Operator Run the following command to enable the operator to monitor the namespace. @@ -275,7 +258,7 @@ Run the following command to enable the operator to monitor the namespace. kubectl label namespace default weblogic-operator=enabled ``` -##### Create WebLogic Domain +#### Create WebLogic Domain Now, you deploy a `sample-domain1` domain resource and an associated `sample-domain1-cluster-1` cluster resource using a single YAML resource file which defines both resources. The domain resource and cluster resource tells the operator how to deploy a WebLogic domain. They do not replace the traditional WebLogic configuration files, but instead cooperate with those files to describe the Kubernetes artifacts of the corresponding domain. - Run the following commands to generate resource files. @@ -391,7 +374,7 @@ The domain resource references the cluster resource, a WebLogic Server installat To access the sample application on WLS, skip to the section [Access sample application](#access-sample-application). The next section includes a script that automates all of the preceding steps. -#### Automation +### Automation If you want to automate the above steps of creating the AKS cluster and WLS domain, you can use the script `${BASE_DIR}/sample-scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh`. @@ -418,7 +401,7 @@ To interact with the cluster using `kubectl`, use `az aks get-credentials` as sh {{% notice info %}} You now have created an AKS cluster with Azure Files NFS share to contain the WLS domain configuration files. Using those artifacts, you have used the operator to create a WLS domain. {{% /notice %}} -#### Access sample application +### Access sample application Access the Administration Console using the admin load balancer IP address. @@ -463,7 +446,7 @@ Found 0 local data sources:

    ``` -#### Validate NFS volume +### Validate NFS volume There are several approaches to validate the NFS volume: @@ -485,7 +468,7 @@ wlsstorage1612795811.file.core.windows.net:/wlsstorage1612795811/wls-weblogic-16 ... ``` -#### Clean up resources +### Clean up resources {{< readfile file="/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt" >}} @@ -493,10 +476,10 @@ If you created the AKS cluster step by step, run the following command to clean {{< readfile file="/samples/azure-kubernetes-service/includes/clean-up-resources-body-02.txt" >}} -#### Troubleshooting +### Troubleshooting For troubleshooting advice, see [Troubleshooting]({{< relref "/samples/azure-kubernetes-service/troubleshooting.md" >}}). -#### Useful links +### Useful links - [Domain on a PV]({{< relref "/samples/domains/domain-home-on-pv/_index.md" >}}) sample diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt index d0c1a75e184..cffe19f6855 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt @@ -1,5 +1,3 @@ -##### Oracle Container Registry - The following steps will direct you to accept the license agreement for WebLogic Server. Make note of your Oracle Account password and email. This sample pertains to 12.2.1.4, but other versions may work as well. - In a web browser, navigate to https://container-registry.oracle.com and log in using the Oracle Single Sign-On authentication service. If you do not already have SSO credentials, at the top of the page, click the **Sign In** link to create them. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt index efc2a106bc4..53284f70789 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt @@ -1,6 +1,3 @@ - -#### Create the AKS cluster - This sample doesn't enable application routing. If you want to enable application routing, follow [Managed nginx Ingress with the application routing add-on in AKS](https://learn.microsoft.com/azure/aks/app-routing?tabs=default%2Cdeploy-app-default). Run the following command to create the AKS cluster. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt index 6d56823506c..53b0a6c8474 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-aks-cluster-storage.txt @@ -1,12 +1,10 @@ -#### Create storage - Our usage pattern for the operator involves creating Kubernetes "persistent volumes" to allow the WebLogic Server to persist its configuration and data separately from the Kubernetes Pods that run WebLogic Server workloads. You will create an external data volume to access and persist data. There are several options for data sharing as described in [Storage options for applications in Azure Kubernetes Service (AKS)](https://docs.microsoft.com/azure/aks/concepts-storage). You will dynamically create and use a persistent volume with Azure Files NFS share. For details about this full featured cloud storage solution, see the [Azure Files Documentation](https://docs.microsoft.com/azure/aks/azure-files-dynamic-pv). -##### Create an Azure Storage account and NFS share +#### Create an Azure Storage account and NFS share 1. Create an Azure Storage Account. @@ -185,9 +183,9 @@ You will dynamically create and use a persistent volume with Azure Files NFS sha ] ``` -##### Create SC and PVC +#### Create SC and PVC -##### Generated configuration files +#### Generated configuration files Use the following command to generate configuration files. ```shell diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/create-resource-group.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/create-resource-group.txt index 37b1c42f6e9..80a1ca46bb3 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/create-resource-group.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/create-resource-group.txt @@ -1,6 +1,3 @@ - -#### Create Resource Group - Create the resource group by issuing the following commands. ```shell diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt index 16369529d99..119e0782c6b 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/download-samples-zip.txt @@ -1,5 +1,3 @@ -##### Download the WebLogic Kubernetes Operator sample. - Download the WebLogic Kubernetes Operator sample ZIP file. We will use several scripts in this zip file to create a WebLogic domain. This sample was tested with v4.2.8, but should work with the latest release. ```shell diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt index 16da7acbbc2..e13fd97dc42 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-01.txt @@ -1,5 +1,3 @@ -#### Prerequisites - This sample assumes the following prerequisite environment. * If you don't have an [Azure subscription](https://learn.microsoft.com/en-us/azure/guides/developer/azure-developer-guide#understanding-accounts-subscriptions-and-billing), create a [free account](https://azure.microsoft.com/free/?ref=microsoft.com&utm_source=microsoft.com&utm_medium=docs&utm_campaign=visualstudio) before you begin. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt index 05674931eac..d9efb645cbe 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/prerequisites-02.txt @@ -1,5 +1,3 @@ -#### Prerequisites - This sample assumes the following prerequisite environment. * If you don't have an [Azure subscription](https://learn.microsoft.com/en-us/azure/guides/developer/azure-developer-guide#understanding-accounts-subscriptions-and-billing), create a [free account](https://azure.microsoft.com/free/?ref=microsoft.com&utm_source=microsoft.com&utm_medium=docs&utm_campaign=visualstudio) before you begin. diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt index 09dc349c697..6043934aeba 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/sign-in-azure.txt @@ -1,5 +1,3 @@ -##### Sign in with Azure CLI - The steps in this section show you how to sign in to the Azure CLI. 1. Open a Bash shell. diff --git a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md index df986521d7a..bc49c5224c8 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md +++ b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md @@ -7,22 +7,13 @@ description: "Sample for creating a WebLogic cluster on the Azure Kubernetes Ser This sample demonstrates how to use the [WebLogic Kubernetes Operator](https://oracle.github.io/weblogic-kubernetes-operator) (hereafter "the operator") to set up a WebLogic Server (WLS) cluster on the Azure Kubernetes Service (AKS) using the model in image domain home source type. After going through the steps, your WLS domain runs on an AKS cluster instance and you can manage your WLS domain by interacting with the operator. -#### Contents - - - [Prerequisites](#prerequisites) - - [Create an AKS cluster](#create-the-aks-cluster) - - [Install WebLogic Kubernetes Operator](#install-weblogic-kubernetes-operator) - - [Create Docker image](#create-docker-image) - - [Create WebLogic domain](#create-weblogic-domain) - - [Invoke the web application](#invoke-the-web-application) - - [Rolling updates](#rolling-updates) - - [Clean up resource](#clean-up-resources) - - [Troubleshooting](#troubleshooting) - - [Useful links](#useful-links) +{{< table_of_contents >}} + +### Prerequisites {{< readfile file="/samples/azure-kubernetes-service/includes/prerequisites-02.txt" >}} -##### Prepare parameters +#### Prepare parameters Set required parameters by running the following commands. @@ -47,20 +38,30 @@ export WEBLOGIC_PASSWORD=Secret123456 export WEBLOGIC_WDT_PASSWORD=Secret123456 ``` +#### Oracle Container Registry + {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-01.txt" >}} +#### Sign in with Azure CLI + {{< readfile file="/samples/azure-kubernetes-service/includes/sign-in-azure.txt" >}} +#### Download the WebLogic Kubernetes Operator sample + {{< readfile file="/samples/azure-kubernetes-service/includes/download-samples-zip.txt" >}} +### Create Resource Group + {{< readfile file="/samples/azure-kubernetes-service/includes/create-resource-group.txt" >}} +### Create the AKS cluster + {{< readfile file="/samples/azure-kubernetes-service/includes/create-aks-cluster-body-02.txt" >}} **NOTE**: If you run into VM size failure, see [Troubleshooting - Virtual Machine size is not supported]({{< relref "/samples/azure-kubernetes-service/troubleshooting#virtual-machine-size-is-not-supported" >}}). -#### Install WebLogic Kubernetes Operator +### Install WebLogic Kubernetes Operator The WebLogic Kubernetes Operator is an adapter to integrate WebLogic Server and Kubernetes, allowing Kubernetes to serve as container infrastructure hosting WLS instances. The operator runs as a Kubernetes Pod and stands ready to perform actions related to running WLS on Kubernetes. @@ -166,17 +167,9 @@ You can specify the operator image by changing value of `--set image`. If you ru If you have an image built with domain models following [Model in Image]({{< relref "/samples/domains/model-in-image/_index.md" >}}), you can go to [Create WebLogic domain](#create-weblogic-domain) directly. {{% /notice %}} -#### Create Docker image +### Create Docker image - - [Image creation prerequisites](#image-creation-prerequisites) - - [Image creation - Introduction](#image-creation---introduction) - - [Understanding your first archive](#understanding-your-first-archive) - - [Staging a ZIP file of the archive](#staging-a-zip-file-of-the-archive) - - [Staging model files](#staging-model-files) - - [Creating the image with WIT](#creating-the-image-with-wit) - - [Pushing the image to Azure Container Registry](#pushing-the-image-to-azure-container-registry) - -##### Image creation prerequisites +#### Image creation prerequisites - The `JAVA_HOME` environment variable must be set and must reference a valid JDK 8 or 11 installation. - Copy the sample to a new directory; for example, use the directory `/tmp/mii-sample`. In the directory name, `mii` is short for "model in image". Model in image is one of three domain home source types supported by the operator. To learn more, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). @@ -201,16 +194,16 @@ If you have an image built with domain models following [Model in Image]({{< rel {{< readfile file="/samples/azure-kubernetes-service/includes/download-wls-tools.txt" >}} -##### Image creation - Introduction +#### Image creation - Introduction {{< readfile file="/samples/azure-kubernetes-service/includes/auxiliary-image-directory.txt" >}} -##### Understanding your first archive +#### Understanding your first archive See [Understanding your first archive]({{< relref "/samples/domains/model-in-image/auxiliary-image-creation#understand-your-first-archive" >}}). -##### Staging a ZIP file of the archive +#### Staging a ZIP file of the archive When you create the image, you will use the files in the staging directory, `${WDT_MODEL_FILES_PATH}/WLS-v1`. In preparation, you need it to contain a ZIP file of the WDT application archive. @@ -228,13 +221,13 @@ $ cd /tmp/mii-sample/archives/archive-v1 $ zip -r ${WDT_MODEL_FILES_PATH}/WLS-v1/archive.zip wlsdeploy ``` -##### Staging model files +#### Staging model files {{< readfile file="/samples/azure-kubernetes-service/includes/staging-model-files.txt" >}} A Model in Image image can contain multiple properties files, archive ZIP files, and YAML files but in this sample you use just one of each. For a complete description of Model in Images model file naming conventions, file loading order, and macro syntax, see [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}) in the Model in Image user documentation. -##### Creating the image with WIT +#### Creating the image with WIT {{< readfile file="/samples/azure-kubernetes-service/includes/run-mii-to-create-auxiliary-image.txt" >}} @@ -246,7 +239,7 @@ The `imagetool.sh` is not supported on macOS with Apple Silicon. See [Troublesho You may run into a `Dockerfile` parsing error if your Docker buildkit is enabled, see [Troubleshooting - WebLogic Image Tool failure]({{< relref "/samples/azure-kubernetes-service/troubleshooting#weblogic-image-tool-failure" >}}). {{% /notice %}} -##### Pushing the image to Azure Container Registry +#### Pushing the image to Azure Container Registry {{< readfile file="/samples/azure-kubernetes-service/includes/create-acr.txt" >}} @@ -270,12 +263,7 @@ The push refers to repository [contosorgresourcegroup1610068510.azurecr.io/mii-a If you see an error that seems related to you not being an **Owner on this subscription**, please refer to the troubleshooting section [Cannot attach ACR due to not being Owner of subscription]({{< relref "/samples/azure-kubernetes-service/troubleshooting#cannot-attach-acr-due-to-not-being-owner-of-subscription" >}}). -#### Create WebLogic domain - - - [Namespace](#namespace) - - [Kubernetes Secrets for WebLogic image](#kubernetes-secrets-for-weblogic-image) - - [Kubernetes Secrets for WebLogic](#kubernetes-secrets-for-weblogic) - - [Domain resource](##domain-resource) +### Create WebLogic domain In this section, you will deploy the new image to the namespace `sample-domain1-ns`, including the following steps: @@ -289,7 +277,7 @@ In this section, you will deploy the new image to the namespace `sample-domain1- - Deploy a domain YAML file that references the new image. - Wait for the domain’s pods to start and reach their ready state. -##### Namespace +#### Namespace Create a namespace that can host one or more domains: @@ -303,7 +291,7 @@ Label the domain namespace so that the operator can autodetect and create WebLog $ kubectl label namespace sample-domain1-ns weblogic-operator=enabled ``` -##### Kubernetes Secrets for WebLogic image +#### Kubernetes Secrets for WebLogic image You will use the `kubernetes/samples/scripts/create-kubernetes-secrets/create-docker-credentials-secret.sh` script to create the Docker credentials as a Kubernetes secret to pull image from OCR. Please run: @@ -323,7 +311,7 @@ secret/wlsregcred created The secret wlsregcred has been successfully created in the sample-domain1-ns namespace. ``` -##### Kubernetes Secrets for WebLogic +#### Kubernetes Secrets for WebLogic First, create the secrets needed by the WLS type model domain. For more on secrets in the context of running domains, see [Prepare to run a domain]({{< relref "/managing-domains/prepare" >}}). In this case, you have two secrets. @@ -395,7 +383,7 @@ sample-domain1-weblogic-credentials Opaque 2 wlsregcred kubernetes.io/dockerconfigjson 1 47s ``` -##### Domain resource +#### Domain resource Now, you create a domain YAML file. Think of the domain YAML file as the way to configure some aspects of your WebLogic domain using Kubernetes. The operator uses the Kubernetes "custom resource" feature to define a Kubernetes resource type called `Domain`. For more on the `Domain` Kubernetes resource, see [Domain Resource]({{< relref "/managing-domains/domain-resource" >}}). For more on custom resources see [the Kubernetes documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). @@ -472,9 +460,9 @@ It may take you up to 10 minutes to deploy all pods, please wait and make sure e If the system does not reach this state, troubleshoot and resolve the problem before continuing. See [Troubleshooting](#troubleshooting) for hints. -#### Invoke the web application +### Invoke the web application -##### Create Azure load balancer +#### Create Azure load balancer Create an Azure public standard load balancer to access the WebLogic Server Administration Console and applications deployed to the cluster. @@ -702,7 +690,7 @@ Events: ``` {{% /expand %}} -##### Access the application +#### Access the application Access the Administration Console using the admin load balancer IP address. @@ -748,25 +736,25 @@ Found 0 local data sources:
    ``` -#### Rolling updates +### Rolling updates Naturally, you will want to deploy newer versions of the EAR application, located in the WDT archive ZIP file at `wlsdeploy/applications/myapp-v1`. To learn how to do this, follow the steps in [Update 3]({{< relref "/samples/domains/model-in-image/update3" >}}). -#### Database connection +### Database connection For guidance on how to connect a database to your AKS with WebLogic Server application, see [Deploy a Java application with WebLogic Server on an Azure Kubernetes Service (AKS) cluster](https://learn.microsoft.com/en-us/azure/aks/howto-deploy-java-wls-app). -#### Clean up resources +### Clean up resources Run the following commands to clean up resources. {{< readfile file="/samples/azure-kubernetes-service/includes/clean-up-resources-body-02.txt" >}} -#### Troubleshooting +### Troubleshooting For troubleshooting advice, see [Troubleshooting]({{< relref "/samples/azure-kubernetes-service/troubleshooting.md" >}}). -#### Useful links +### Useful links - [Model in Image]({{< relref "/managing-domains/model-in-image/_index.md" >}}) user documentation - [Model in Image]({{< relref "/samples/domains/model-in-image/_index.md" >}}) sample diff --git a/documentation/site/static/images/aks-solution.png b/documentation/site/static/images/aks-solution.png index 77455ab3a6e8539617a2cd3e53c357cb9371185e..7e544a1ab8a2f2dac09a551bdfb84287e5068ecf 100644 GIT binary patch literal 95995 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

    G=lrf* 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 V_e*pZo0R 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

    uXr|jzs#YpsV!iIm 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 KWD2F;e*Ry=!ObiH From c6b15838e45373c3fbbb12967601446b3d18035a Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Mon, 2 Dec 2024 22:13:43 +0000 Subject: [PATCH 238/356] backport OWLS-121128 to release4.2 --- .../java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java index d9ef37ef99a..7394b40146b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java @@ -547,7 +547,7 @@ private static boolean findStringInDomainStatusServerHealth(String regex) { private OffsetDateTime verifyServersStartedConcurrently(String managedServerPodNamePrefix, int startPodNum, int endPodNum) { - final int deltaValue = 60; // seconds + final int deltaValue = 120; OffsetDateTime msPodCreationTime = null; // get managed server pod creation time From b2016451dff89b30efa81a2700c85546c22d45b2 Mon Sep 17 00:00:00 2001 From: jshum Date: Wed, 4 Dec 2024 08:02:50 -0600 Subject: [PATCH 239/356] Correct online update going through upgrade logic by mistake --- operator/src/main/resources/scripts/modelInImage.sh | 2 +- operator/src/main/resources/scripts/model_wdt_mii_filter.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 48098b99298..16e6ee0fde9 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -396,7 +396,7 @@ createWLDomain() { # create domain again DISABLE_SM_FOR_12214_NONSM_UPG=0 - if [ -f ${PRIMORDIAL_DOMAIN_ZIPPED} ] ; then + if [ -f ${PRIMORDIAL_DOMAIN_ZIPPED} ] && [ -z "${MII_USE_ONLINE_UPDATE}" ] || [ "${MII_USE_ONLINE_UPDATE}" != "true" ] ]; then checkSecureModeForUpgrade fi if [ ${WDT_ARTIFACTS_CHANGED} -ne 0 ] || [ ${jdk_changed} -eq 1 ] \ diff --git a/operator/src/main/resources/scripts/model_wdt_mii_filter.py b/operator/src/main/resources/scripts/model_wdt_mii_filter.py index cc653966c00..bc6f077e448 100644 --- a/operator/src/main/resources/scripts/model_wdt_mii_filter.py +++ b/operator/src/main/resources/scripts/model_wdt_mii_filter.py @@ -410,6 +410,7 @@ def upgradeServerIfNeeded(model): result = fh.read() fh.close() found = False + # if secure mode is not enabled in existing domain if result == 'False': # check if model has anything set # if domainInfo already set to secure or in dev mode then do not set it, prod mode will not be secure From 4c442c3072b72722ef79c8f5b4ccb2507985b574 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 6 Dec 2024 16:49:06 +0000 Subject: [PATCH 240/356] Fix kind-sequential nightly run failures --- Jenkinsfile.podman | 3 +- integration-tests/pom.xml | 36 ------------------- .../ItOnPremCrossDomainTransaction.java | 5 ++- .../weblogic/kubernetes/TestConstants.java | 5 +++ .../extensions/InitializationTasks.java | 18 ++++++++++ .../resources/bash-scripts/install-istio.sh | 16 ++++++--- .../resources/bash-scripts/install-wls.sh | 27 ++++++-------- 7 files changed, 48 insertions(+), 62 deletions(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index bf1b8fd4308..cf57b94e8b5 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -569,7 +569,7 @@ EOF echo "-DWLSIMG_BUILDER=\"podman\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config echo "-Djdk.httpclient.allowRestrictedHeaders=\"host\"" >> ${WORKSPACE}/.mvn/maven.config - echo "-Dinstall.weblogic=\"true\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.install.weblogic=\"true\"" >> ${WORKSPACE}/.mvn/maven.config echo "${WORKSPACE}/.mvn/maven.config contents:" cat "${WORKSPACE}/.mvn/maven.config" @@ -589,7 +589,6 @@ EOF export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" - export SHIPHOME_DOWNLOAD_SERVER="home.us.oracle.com" if ! time mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/kindtest.log"; then echo "integration-tests failed" exit 1 diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index a6bf50f8471..3ec2cd94413 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -765,41 +765,5 @@ toolkits-srg - - - integration-test-env - - - - install.weblogic - true - - - - - - org.codehaus.mojo - exec-maven-plugin - - - run-silent-install-script - pre-integration-test - - exec - - - bash - - ${project.basedir}/src/test/resources/bash-scripts/install-wls.sh - ${wko.it.result.root} - ${wko.it.shiphome.download.server} - - - - - - - - diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java index 8b53ac4df7e..4e7f8844d23 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java @@ -49,7 +49,6 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; -import static oracle.weblogic.kubernetes.TestConstants.RESULTS_BASE; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; @@ -449,10 +448,10 @@ private static void createDomainResource(String domainUid, String domNamespace, private static void createOnPremDomain() throws IOException, InterruptedException { logger.info("creating on premise domain"); Path createDomainScript = downloadAndInstallWDT(); - mwHome = Path.of(RESULTS_BASE, "mwhome"); + mwHome = Path.of(RESULTS_ROOT, "mwhome"); String modelFileList = RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN2 + "," + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS2; - domainHome = Path.of(RESULTS_BASE, "mwhome", "domains", "domain2"); + domainHome = Path.of(RESULTS_ROOT, "mwhome", "domains", "domain2"); logger.info("creating on premise domain home {0}", domainHome); Files.createDirectories(domainHome); Path modelProperties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN2_PROPS); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 86d912758a3..c044618cff5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -477,6 +477,11 @@ public interface TestConstants { public static final String LARGE_DOMAIN_TESTING_PROPS_FILE = "largedomaintesting.props"; + public static final boolean INSTALL_WEBLOGIC = Boolean.valueOf((getNonEmptySystemProperty("wko.it.install.weblogic", + "false"))); + public static final String WEBLOGIC_SHIPHOME = getNonEmptySystemProperty("wko.it.wls.shiphome", + "https://home.us.oracle.com/results/release/src141200/fmw_14.1.2.0.0_wls_generic.jar"); + public static final String ORACLE_OPERATOR_NS = "ns-oracle-operator"; //node ports used by the integration tests diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index e08757ad7a7..ed6e243a72a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -56,6 +56,7 @@ import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.INGRESS_CLASS_FILE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.INSTALL_WEBLOGIC; import static oracle.weblogic.kubernetes.TestConstants.KIND_REPO; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.LOCALE_IMAGE_NAME; @@ -86,6 +87,7 @@ import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_MODEL_PROPERTIES_FILE; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_SHIPHOME; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.DOWNLOAD_DIR; @@ -107,6 +109,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.imageRepoLogin; import static oracle.weblogic.kubernetes.actions.TestActions.imageTag; import static oracle.weblogic.kubernetes.actions.TestActions.uninstallOperator; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Command.defaultCommandParams; import static oracle.weblogic.kubernetes.assertions.TestAssertions.doesImageExist; import static oracle.weblogic.kubernetes.assertions.TestAssertions.imageExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; @@ -329,6 +332,9 @@ public void beforeAll(ExtensionContext context) { logger.info("Installing istio before any test suites are run"); installIstio(); } + if (INSTALL_WEBLOGIC && !OKD && !CRIO && !ARM && !OKE_CLUSTER) { + installOnPremWebLogic(); + } } finally { // Initialization is done. Release all waiting other threads. The latch is now disabled so // other threads @@ -702,4 +708,16 @@ private void installOracleDBOperator() { assertDoesNotThrow(() -> installDBOperator(), "Failed to install database operator"); } + private void installOnPremWebLogic() { + Path installScript = Paths.get(RESOURCE_DIR, "bash-scripts", "install-wls.sh"); + String command + = String.format("%s %s %s %s", "/bin/bash", installScript, RESULTS_ROOT, WEBLOGIC_SHIPHOME); + getLogger().info("WebLogic installation command {0}", command); + assertTrue(() -> Command.withParams( + defaultCommandParams() + .command(command) + .redirect(false)) + .execute()); + } + } diff --git a/integration-tests/src/test/resources/bash-scripts/install-istio.sh b/integration-tests/src/test/resources/bash-scripts/install-istio.sh index fe502b0ffd9..a013b5d332b 100755 --- a/integration-tests/src/test/resources/bash-scripts/install-istio.sh +++ b/integration-tests/src/test/resources/bash-scripts/install-istio.sh @@ -33,10 +33,18 @@ ${KUBERNETES_CLI} delete namespace istio-system --ignore-not-found # create the namespace 'istio-system' ${KUBERNETES_CLI} create namespace istio-system -( cd $workdir; - oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ - --name=istio/istio-${version}-${arch}.tar.gz --file=istio.tar.gz \ - --auth=instance_principal +( cd $workdir + if [ -z "$JENKINS_HOME" ]; then + # Not in Jenkins, download using curl + echo "Detected local environment. Downloading Istio using curl." + curl -Lo istio.tar.gz https://github.com/istio/istio/releases/download/${version}/istio-${version}-${arch}.tar.gz + else + # Running in Jenkins, download using OCI CLI + echo "Detected Jenkins environment. Downloading Istio using OCI CLI." + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=istio/istio-${version}-${arch}.tar.gz --file=istio.tar.gz \ + --auth=instance_principal + fi tar zxf istio.tar.gz ) diff --git a/integration-tests/src/test/resources/bash-scripts/install-wls.sh b/integration-tests/src/test/resources/bash-scripts/install-wls.sh index baf777de54e..daa19af2dac 100644 --- a/integration-tests/src/test/resources/bash-scripts/install-wls.sh +++ b/integration-tests/src/test/resources/bash-scripts/install-wls.sh @@ -3,36 +3,29 @@ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo $JAVA_HOME -if [ -n "$1" ] || [ -n "$result_root" ]; then - echo "RESULT_ROOT or result_root is set" +if [ -n "$1" ]; then + echo "RESULT_ROOT is set" + result_root=$1 else - echo "no RESULT_ROOT or result_root is set, exiting wls installation." + echo "no RESULT_ROOT is set, exiting wls installation." exit 0 fi -if [ -n "$2" ] || [ -n "$SHIPHOME_DOWNLOAD_SERVER" ]; then - echo "SHIPHOME_DOWNLOAD_SERVER is set" +if [ -n "$2" ]; then + echo "WEBLOGIC_SHIPHOME is set" + shiphome_url=$2 else - echo "no SHIPHOME_DOWNLOAD_SERVER is set, exiting wls installation." + echo "no WEBLOGIC_SHIPHOME is set, exiting wls installation." exit 0 fi -if [ -z "$1" ]; then - echo "running in Jenkins, result_root is set" -else - echo "running in localhost, RESULT_ROOT is set : $1" - result_root=$1 - SHIPHOME_DOWNLOAD_SERVER=$2 - -fi echo $result_root -echo $SHIPHOME_DOWNLOAD_SERVER +echo $shiphome_url MW_HOME="$result_root/mwhome" SILENT_RESPONSE_FILE=$result_root/silent.response ORAINVENTORYPOINTER_LOC=$result_root/oraInv.loc ORAINVENTORY_LOC=$result_root/oraInventory WLS_SHIPHOME=$result_root/fmw_wls_generic.jar -DOWNLOAD_URL="http://$SHIPHOME_DOWNLOAD_SERVER/results/release/src141200/fmw_14.1.2.0.0_wls_generic.jar" SUCCESS="The\ installation\ of\ Oracle\ Fusion\ Middleware.*completed\ successfully" rm -rf $MW_HOME/* @@ -64,7 +57,7 @@ EOF cat $ORAINVENTORYPOINTER_LOC #download WebLogic shiphome installer -curl -Lo $WLS_SHIPHOME $DOWNLOAD_URL +curl -Lo $WLS_SHIPHOME $shiphome_url ls -l $WLS_SHIPHOME md5sum $WLS_SHIPHOME From 089332feb51e506b82f3d0a36105e960b980593e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 9 Dec 2024 11:02:27 -0500 Subject: [PATCH 241/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b19eab18c04..38fc2c5b56b 100644 --- a/pom.xml +++ b/pom.xml @@ -703,7 +703,7 @@ 2.0.1 1.0.39 1.9.0 - 1.5.3 + 1.5.4 1.4.0 1.17.1 1.7.3 @@ -731,14 +731,14 @@ 4.0.2 6.1.0 0.16.0 - 2.18.1 + 2.18.2 2.18.1 2.3 2.11.0 11.1.0 2.0.16 1.5.12 - 4.28.3 + 4.29.1 2.5.1 9.47 ${project.basedir}/src-generated-swagger From f19a864836c5879a3bf6490c88ca7bab48cdd932 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 9 Dec 2024 19:07:42 +0000 Subject: [PATCH 242/356] Merge branch 'owls-120737' into 'main' Correct Helm chart usage of enableClusterRoleBinding See merge request weblogic-cloud/weblogic-kubernetes-operator!4868 (cherry picked from commit f88e4240b3755c6d26ae0b51a4674b0cf67fe18f) 382bed5b Correct usage of cluster roles 4be70c75 Work in progress 582b9210 Merge remote-tracking branch 'origin/main' into owls-120737 e27d172b Merge remote-tracking branch 'origin/main' into owls-120737 6e511786 Merge remote-tracking branch 'origin/main' into owls-120737 422280cb Work in progress 03e4fc6f Work in progress 35027b88 Test passing dbd928fe Merge remote-tracking branch 'origin/main' into owls-120737 217b81db Corrections --- .../_operator-clusterrole-domain-admin.tpl | 2 +- .../templates/_operator-clusterrole-general.tpl | 2 +- .../_operator-clusterrole-namespace.tpl | 2 +- .../_operator-clusterrole-operator-admin.tpl | 2 +- .../_operator-clusterrolebinding-general.tpl | 2 +- .../_operator-rolebinding-namespace.tpl | 7 ++++--- .../weblogic-operator/templates/_operator.tpl | 9 +++++---- kubernetes/charts/weblogic-operator/values.yaml | 6 +++++- .../CreateOperatorGeneratedFilesTestBase.java | 16 ++++++---------- .../operator/helm/HelmOperatorValues.java | 4 +--- .../operator/utils/GeneratedOperatorObjects.java | 6 +++--- .../ParsedWeblogicOperatorSecurityYaml.java | 5 +++-- 12 files changed, 32 insertions(+), 31 deletions(-) diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-domain-admin.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-domain-admin.tpl index 9f1d6884560..a5b5f0376ff 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-domain-admin.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-domain-admin.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorClusterRoleDomainAdmin" }} diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-general.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-general.tpl index 239a2ad8de9..b2cc92da785 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-general.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-general.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorClusterRoleGeneral" }} diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-namespace.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-namespace.tpl index b91e082a188..45e23fc4aba 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-namespace.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-namespace.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorClusterRoleNamespace" }} diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-operator-admin.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-operator-admin.tpl index 31c27900696..3b83fc76ca5 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-operator-admin.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrole-operator-admin.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorClusterRoleOperatorAdmin" }} diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrolebinding-general.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrolebinding-general.tpl index dce0ae1d6df..4afb9018c16 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-clusterrolebinding-general.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-clusterrolebinding-general.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.clusterRoleBindingGeneral" }} diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-rolebinding-namespace.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-rolebinding-namespace.tpl index ead5d1d778b..ed62a947631 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-rolebinding-namespace.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-rolebinding-namespace.tpl @@ -1,16 +1,17 @@ -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorRoleBindingNamespace" }} --- -{{- if and (or .enableClusterRoleBinding (not (hasKey . "enableClusterRoleBinding"))) (ne .domainNamespaceSelectionStrategy "Dedicated") }} +{{- $useClusterRole := and (or .enableClusterRoleBinding (not (hasKey . "enableClusterRoleBinding"))) (not (eq .domainNamespaceSelectionStrategy "Dedicated")) }} +{{- if $useClusterRole }} kind: "ClusterRoleBinding" {{- else }} kind: "RoleBinding" {{- end }} apiVersion: "rbac.authorization.k8s.io/v1" metadata: - {{- if and (or .enableClusterRoleBinding (not (hasKey . "enableClusterRoleBinding"))) (ne .domainNamespaceSelectionStrategy "Dedicated") }} + {{- if $useClusterRole }} name: {{ list .Release.Namespace "weblogic-operator-clusterrolebinding-namespace" | join "-" | quote }} {{- else }} name: "weblogic-operator-rolebinding-namespace" diff --git a/kubernetes/charts/weblogic-operator/templates/_operator.tpl b/kubernetes/charts/weblogic-operator/templates/_operator.tpl index ed98f7eb8c9..f77479f449f 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- if and (not (empty .Capabilities.APIVersions)) (not (.Capabilities.APIVersions.Has "policy/v1")) }} @@ -28,9 +28,10 @@ {{- include "operator.operatorInternalService" . }} {{- include "operator.operatorExternalService" . }} {{- include "operator.operatorWebhookExternalService" . }} -{{- if or (not .enableClusterRoleBinding) (eq .domainNamespaceSelectionStrategy "Dedicated") }} -{{- include "operator.domainNamespaces" . }} -{{- else }} +{{- $useClusterRole := and (or .enableClusterRoleBinding (not (hasKey . "enableClusterRoleBinding"))) (not (eq .domainNamespaceSelectionStrategy "Dedicated")) }} +{{- if $useClusterRole }} {{- include "operator.operatorRoleBindingNamespace" . }} +{{- else }} +{{- include "operator.domainNamespaces" . }} {{- end }} {{- end }} diff --git a/kubernetes/charts/weblogic-operator/values.yaml b/kubernetes/charts/weblogic-operator/values.yaml index 97584c83239..a3fc098b676 100644 --- a/kubernetes/charts/weblogic-operator/values.yaml +++ b/kubernetes/charts/weblogic-operator/values.yaml @@ -48,8 +48,12 @@ domainNamespaceSelectionStrategy: LabelSelector # enableClusterRoleBinding specifies whether the roles necessary for the operator to manage domains # will be granted using a ClusterRoleBinding rather than using RoleBindings in each managed namespace. -# If not specified, the default is true unless 'domainNamespaceSelectionStrategy' is 'Dedicated, in which +# If not specified, the default is true unless 'domainNamespaceSelectionStrategy' is 'Dedicated', in which # case this value is ignored as all resources will be created in the namespace where the operator is deployed. +# If `enableClusterRoleBinding` is false but `domainNamespaceSelectionStrategy` is not `Dedicated` then +# ClusterRoleBindings will still be created to allow the operator to manage the CRDs and to list namespaces. +# No ClusterRoles or ClusterRoleBindings will be created when `enableClusterRoleBinding` is false and +# `domainNamespaceSelectionStrategy` is `Dedicated`. # enableClusterRoleBinding: true diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java index c75df7a9cfc..9e400f0efc0 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java @@ -651,20 +651,16 @@ private V1ClusterRole getExpectedWeblogicOperatorNamespaceRole() { @Test void generatesCorrect_domainNamespaces_weblogicOperatorRoleBindings() { - for (String domainNamespace : getInputs().getDomainNamespaces().split(",")) { - String namespace = domainNamespace.trim(); - assertThat( - getGeneratedFiles().getWeblogicOperatorRoleBinding(namespace), - equalTo(getExpectedWeblogicOperatorRoleBinding(namespace))); - } + assertThat( + getGeneratedFiles().getWeblogicOperatorClusterRoleBinding(), + equalTo(getExpectedWeblogicOperatorClusterRoleBinding())); } - private V1RoleBinding getExpectedWeblogicOperatorRoleBinding(String namespace) { - return newRoleBinding() + private V1ClusterRoleBinding getExpectedWeblogicOperatorClusterRoleBinding() { + return newClusterRoleBinding() .metadata( newObjectMeta() - .name("weblogic-operator-rolebinding-namespace") - .namespace(namespace) + .name(getInputs().getNamespace() + "-weblogic-operator-clusterrolebinding-namespace") .putLabelsItem(OPERATORNAME_LABEL, getInputs().getNamespace())) .addSubjectsItem( newSubject() diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/helm/HelmOperatorValues.java b/kubernetes/src/test/java/oracle/kubernetes/operator/helm/HelmOperatorValues.java index 4a18250707f..0734af176d2 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/helm/HelmOperatorValues.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/helm/HelmOperatorValues.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helm; @@ -103,8 +103,6 @@ private void loadImagePullSecretsFromMap(Map map) { Map createMap() { HashMap map = new HashMap<>(); - map.put("enableClusterRoleBinding", Boolean.FALSE); - addStringMapEntry(map, this::getServiceAccount, "serviceAccount"); addStringMapEntry(map, this::getWeblogicOperatorImage, "image"); addStringMapEntry(map, this::getJavaLoggingLevel, "javaLoggingLevel"); diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/GeneratedOperatorObjects.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/GeneratedOperatorObjects.java index fbccc2eda64..fc9be207b02 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/GeneratedOperatorObjects.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/GeneratedOperatorObjects.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.utils; @@ -86,8 +86,8 @@ public V1ClusterRole getWeblogicOperatorNamespaceRole() { return securityYaml.getWeblogicOperatorNamespaceRole(); } - public V1RoleBinding getWeblogicOperatorRoleBinding(String namespace) { - return securityYaml.getWeblogicOperatorRoleBinding(namespace); + public V1ClusterRoleBinding getWeblogicOperatorClusterRoleBinding() { + return securityYaml.getWeblogicOperatorClusterRoleBinding(); } public V1RoleBinding getWeblogicOperatorRoleBinding() { diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java index e0c20857e51..71e9c1e5f0e 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/utils/ParsedWeblogicOperatorSecurityYaml.java @@ -60,8 +60,9 @@ public V1ClusterRole getWeblogicOperatorNamespaceRole() { .find(inputs.getNamespace() + "-weblogic-operator-clusterrole-namespace"); } - public V1RoleBinding getWeblogicOperatorRoleBinding(String namespace) { - return getRoleBindings().find("weblogic-operator-rolebinding-namespace", namespace); + public V1ClusterRoleBinding getWeblogicOperatorClusterRoleBinding() { + return getClusterRoleBindings() + .find(inputs.getNamespace() + "-weblogic-operator-clusterrolebinding-namespace"); } public V1RoleBinding getWeblogicOperatorRoleBinding() { From 60463eacd7fa6c799d3ecf8f99a5599f06d94213 Mon Sep 17 00:00:00 2001 From: jshum Date: Thu, 12 Dec 2024 15:24:30 -0600 Subject: [PATCH 243/356] Clarify error message when clusterName specified in cluster spec does not exists. --- common/src/main/resources/Operator.properties | 2 +- .../operator/helpers/WlsConfigValidator.java | 16 ++++++++++------ .../helpers/TopologyValidationStepTest.java | 8 ++++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/common/src/main/resources/Operator.properties b/common/src/main/resources/Operator.properties index 7da870f3804..518973ac210 100644 --- a/common/src/main/resources/Operator.properties +++ b/common/src/main/resources/Operator.properties @@ -199,7 +199,7 @@ WLSDO-0007=Configuration overridesConfigMap ''{0}'' is not supported if ''domain WLSDO-0008=ConfigMap ''{0}'' specified by ''{1}'' not found in namespace ''{2}''. WLSDO-0009=The secret ''{0}'' must be specified if ''domainHomeSourceType'' is configured as ''FromModel''. WLSDO-0010=The secret ''{0}'' must be specified if ''domainHomeSourceType'' is configured as ''FromModel'' and spec.configuration.model.domainType is configured as ''JRF''. -WLSDO-0011=Cluster ''{0}'' specified in the domain resource does not exist in the WebLogic domain home configuration. +WLSDO-0011=The clusterName ''{0}'' specified in the cluster resource ''{1}'' referenced by the domain resource does not exist in the WebLogic domain home configuration. WLSDO-0012=Managed Server ''{0}'' specified in the domain resource does not exist in the WebLogic domain home configuration. WLSDO-0013=Istio is enabled and the domain resource specified to expose channel {0} in the adminServices section. The channel name default, \ default-admin, default-secure are internal to the WebLogic Kubernetes Operator. Please create the network channel with a different name in the WebLogic Domain before \ diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/WlsConfigValidator.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/WlsConfigValidator.java index ccaaf0c3056..eefdd5a02b7 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/WlsConfigValidator.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/WlsConfigValidator.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import javax.annotation.Nonnull; @@ -21,8 +22,6 @@ import oracle.kubernetes.operator.wlsconfig.WlsDynamicServersConfig; import oracle.kubernetes.operator.wlsconfig.WlsServerConfig; import oracle.kubernetes.operator.work.Packet; -import oracle.kubernetes.weblogic.domain.model.ClusterResource; -import oracle.kubernetes.weblogic.domain.model.ClusterSpec; import oracle.kubernetes.weblogic.domain.model.ManagedServer; import oracle.kubernetes.weblogic.domain.model.MonitoringExporterSpecification; import org.apache.commons.collections4.ListUtils; @@ -128,8 +127,13 @@ List getTopologyFailures() { private void verifyDomainResourceReferences() { getManagedServers().stream() .map(ManagedServer::getServerName).filter(this::isUnknownServer).forEach(this::reportUnknownServer); - info.getReferencedClusters().stream().map(ClusterResource::getSpec) - .map(ClusterSpec::getClusterName).filter(this::isUnknownCluster).forEach(this::reportUnknownCluster); + + info.getReferencedClusters().stream() + .map(cluster -> Map.entry(cluster.getSpec().getClusterName(), + Objects.requireNonNull(cluster.getMetadata().getName()))) + .filter(entry -> isUnknownCluster(entry.getKey())) + .forEach(entry -> reportUnknownCluster(entry.getKey(), entry.getValue())); + } private List getManagedServers() { @@ -155,8 +159,8 @@ private boolean isUnknownCluster(String clusterName) { return !domainConfig.containsCluster(clusterName); } - private void reportUnknownCluster(String clusterName) { - reportFailure(NO_CLUSTER_IN_DOMAIN, clusterName); + private void reportUnknownCluster(String wlsClusterName, String refClusterName) { + reportFailure(NO_CLUSTER_IN_DOMAIN, wlsClusterName, refClusterName); } // Ensure that generated service names are valid. diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/TopologyValidationStepTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/TopologyValidationStepTest.java index ff778b639ec..fb46ba41221 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/TopologyValidationStepTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/TopologyValidationStepTest.java @@ -463,7 +463,7 @@ void whenClusterDoesNotExistInDomain_logWarning() { runTopologyValidationStep(); - assertTopologyMismatchReported(NO_CLUSTER_IN_DOMAIN, "no-such-cluster"); + assertTopologyMismatchReported(NO_CLUSTER_IN_DOMAIN, "no-such-cluster", "no-such-cluster"); } @Test @@ -533,7 +533,7 @@ void removeOldReplicasTooHighFailures() { void preserveTopologyFailuresThatStillExist() { consoleControl.ignoreMessage(NO_CLUSTER_IN_DOMAIN); final OffsetDateTime initialTime = SystemClock.now(); - final String message = getFormattedMessage(NO_CLUSTER_IN_DOMAIN, "no-such-cluster"); + final String message = getFormattedMessage(NO_CLUSTER_IN_DOMAIN, "no-such-cluster", "no-such-cluster"); domain.getStatus().addCondition(new DomainCondition(FAILED).withReason(TOPOLOGY_MISMATCH) .withFailureInfo(domain.getSpec()).withMessage(message)); @@ -1027,7 +1027,7 @@ void whenClusterDoesNotExistInDomain_logWarningAndCreateEvent() { runTopologyValidationStep(); - assertTopologyMismatchReported(NO_CLUSTER_IN_DOMAIN, "no-such-cluster"); + assertTopologyMismatchReported(NO_CLUSTER_IN_DOMAIN, "no-such-cluster", "no-such-cluster"); } @Test @@ -1043,7 +1043,7 @@ void whenBothServerAndClusterDoNotExistInDomain_createEventWithBothWarnings() { assertThat(testSupport, hasEvent(DOMAIN_FAILED_EVENT) .withMessageContaining(getLocalizedString(TOPOLOGY_MISMATCH_EVENT_ERROR), - getFormattedMessage(NO_CLUSTER_IN_DOMAIN, "no-such-cluster"), + getFormattedMessage(NO_CLUSTER_IN_DOMAIN, "no-such-cluster", "no-such-cluster"), getFormattedMessage(NO_MANAGED_SERVER_IN_DOMAIN, "no-such-server"))); } From 8f96855d5455873fc729a56fc0da9a0616027f1e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 13 Dec 2024 17:19:11 -0500 Subject: [PATCH 244/356] Webhook handle lack of permission and skip installing webhook secret for operatorOnly --- .../weblogic-operator/templates/_operator-secret.tpl | 6 +++++- .../kubernetes/operator/helpers/WebhookHelper.java | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-secret.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-secret.tpl index 243ebe2178a..f3febdf37fa 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-secret.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-secret.tpl @@ -1,7 +1,8 @@ -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorSecrets" }} +{{- if not .webhookOnly }} --- apiVersion: "v1" kind: "Secret" @@ -22,6 +23,8 @@ metadata: name: "weblogic-operator-secrets" namespace: {{ .Release.Namespace | quote }} type: "Opaque" +{{- end }} +{{- if not .operatorOnly }} --- apiVersion: "v1" kind: "Secret" @@ -44,3 +47,4 @@ metadata: namespace: {{ .Release.Namespace | quote }} type: "Opaque" {{- end }} +{{- end }} diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java index d0ea0226f7c..bf7f246ada9 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/WebhookHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, Oracle and/or its affiliates. +// Copyright (c) 2022, 2024, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -263,7 +263,8 @@ protected NextAction onFailureNoRetry(Packet packet, CallResponse callResponse) { LOGGER.info(MessageKeys.READ_VALIDATING_WEBHOOK_CONFIGURATION_FAILED, VALIDATING_WEBHOOK_NAME, callResponse.getE().getResponseBody()); - return super.onFailureNoRetry(packet, callResponse); + return isNotAuthorizedOrForbidden(callResponse) + ? doNext(packet) : super.onFailureNoRetry(packet, callResponse); } } @@ -284,7 +285,8 @@ protected NextAction onFailureNoRetry(Packet packet, CallResponse callResponse) { LOGGER.info(MessageKeys.CREATE_VALIDATING_WEBHOOK_CONFIGURATION_FAILED, VALIDATING_WEBHOOK_NAME, callResponse.getE().getResponseBody()); - return super.onFailureNoRetry(packet, callResponse); + return isNotAuthorizedOrForbidden(callResponse) + ? doNext(packet) : super.onFailureNoRetry(packet, callResponse); } } @@ -318,7 +320,8 @@ protected NextAction onFailureNoRetry(Packet packet, CallResponse callResponse) { LOGGER.info(MessageKeys.REPLACE_VALIDATING_WEBHOOK_CONFIGURATION_FAILED, VALIDATING_WEBHOOK_NAME, callResponse.getE().getResponseBody()); - return super.onFailureNoRetry(packet, callResponse); + return isNotAuthorizedOrForbidden(callResponse) + ? doNext(packet) : super.onFailureNoRetry(packet, callResponse); } } From 6197cf7098387b7bfffc15b51f4a421478bb7e6b Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 17 Dec 2024 13:50:42 +0000 Subject: [PATCH 245/356] Add cross domain transaction tests with JMS provider in K8s and JMS client in on prem domain --- Jenkinsfile.podman | 2 + .../ItOnPremCrossDomainTransaction.java | 455 +++++++++++++----- .../weblogic/kubernetes/TestConstants.java | 2 + .../kubernetes/actions/impl/AppBuilder.java | 1 + .../weblogic/kubernetes/utils/FileUtils.java | 4 +- .../mdbtopic/src/application/MdbTopic.java | 2 + .../test/resources/onpremcrtx/getmessages.py | 13 + ...-crossdomaintransaction-domain1.properties | 1 - .../model-crossdomaintransaction-domain1.yaml | 2 +- .../model-crossdomaintransaction-domain3.yaml | 38 ++ .../onprem-domain2-node-portrouting.yaml | 22 + 11 files changed, 417 insertions(+), 125 deletions(-) create mode 100644 integration-tests/src/test/resources/onpremcrtx/getmessages.py create mode 100644 integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain3.yaml create mode 100644 integration-tests/src/test/resources/onpremcrtx/onprem-domain2-node-portrouting.yaml diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index cf57b94e8b5..eabf6553331 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -469,6 +469,8 @@ nodes: hostPort: 2228 - containerPort: 31132 hostPort: 8001 + - containerPort: 31136 + hostPort: 2232 extraMounts: - hostPath: ${pv_root} containerPath: ${pv_root} diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java index 4e7f8844d23..eee87e4f588 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOnPremCrossDomainTransaction.java @@ -8,16 +8,18 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.net.http.HttpResponse; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -29,14 +31,16 @@ import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.Model; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.AppParams; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.ExecResult; +import oracle.weblogic.kubernetes.utils.FileUtils; import oracle.weblogic.kubernetes.utils.OracleHttpClient; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; @@ -47,17 +51,26 @@ import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_VERSION; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.IT_ONPREMCRDOMAINTX_CLUSTER_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.IT_ONPREMCRDOMAINTX_CLUSTER_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.DOWNLOAD_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WLS; import static oracle.weblogic.kubernetes.actions.ActionConstants.WORK_DIR; +import static oracle.weblogic.kubernetes.actions.TestActions.archiveApp; import static oracle.weblogic.kubernetes.actions.TestActions.createDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; +import static oracle.weblogic.kubernetes.actions.TestActions.shutdownDomain; import static oracle.weblogic.kubernetes.assertions.TestAssertions.domainExists; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.checkAppIsActive; import static oracle.weblogic.kubernetes.utils.BuildApplication.buildApplication; @@ -91,26 +104,31 @@ class ItOnPremCrossDomainTransaction { private static final String WDT_MODEL_FILE_DOMAIN1 = "model-crossdomaintransaction-domain1.yaml"; private static final String WDT_MODEL_FILE_DOMAIN2 = "model-crossdomaintransaction-domain2.yaml"; + private static final String WDT_MODEL_FILE_DOMAIN3 = "model-crossdomaintransaction-domain3.yaml"; - private static final String WDT_MODEL_DOMAIN1_PROPS = "model-crossdomaintransaction-domain1.properties"; - private static final String WDT_MODEL_DOMAIN2_PROPS = "model-crossdomaintransaction-domain2.properties"; private static final String ONPREM_DOMAIN_ROUTING = "onprem-domain-routing.yaml"; + private static final String K8S_DOMAIN3_NODEPORT_ROUTING = "onprem-domain2-node-portrouting.yaml"; private static String onpremIngressClass = null; private static final String WDT_MODEL_FILE_JMS = "model-cdt-jms.yaml"; private static final String WDT_MODEL_FILE_JMS2 = "model2-cdt-jms.yaml"; - + + private static List applicationsList; private static final String WDT_IMAGE_NAME1 = "domain1-onprem-wdt-image"; private static final String PROPS_TEMP_DIR = RESULTS_ROOT + "/crossdomainonpremtemp"; private static String opNamespace = null; private static String domain1Namespace = null; + private static String domain3Namespace = null; private static String domainUid1 = "domain1"; + private static String domainUid3 = "domain3"; private static String adminServerName = "admin-server"; - private static String managedServerPrefix = domainUid1 + "-managed-server"; + private static int adminServerPort = 7001; + private static LoggingFacade logger = null; private static String hostHeader; private static Map headers = null; private static String hostAndPort = null; + private static String localAddress; private static String javaOptions = "-Dweblogic.Debug.DebugNaming=true " + "-Dweblogic.Debug.DebugJTANaming=true " @@ -125,8 +143,11 @@ class ItOnPremCrossDomainTransaction { + "-Dweblogic.rjvm.allowUnknownHost=true " + "-Dweblogic.security.remoteAnonymousRMIT3Enabled=true"; private static Path wlstScript; + private static Path createDomainScript; private static Path domainHome; private static Path mwHome; + private static String domainUT; + private static String domainNSUT; /** * Install Operator. @@ -134,7 +155,7 @@ class ItOnPremCrossDomainTransaction { * @param namespaces list of namespaces */ @BeforeAll - public static void initAll(@Namespaces(2) List namespaces) + public static void initAll(@Namespaces(3) List namespaces) throws UnknownHostException, IOException, InterruptedException { logger = getLogger(); @@ -146,34 +167,33 @@ public static void initAll(@Namespaces(2) List namespaces) logger.info("Creating unique namespace for Domain"); assertNotNull(namespaces.get(1), "Namespace list is null"); domain1Namespace = namespaces.get(1); + + logger.info("Creating unique namespace for Domain"); + assertNotNull(namespaces.get(2), "Namespace list is null"); + domain3Namespace = namespaces.get(2); + //install traefil for on prem domain onpremIngressClass = installTraefikForPremDomain(); - - // Now that we got the namespaces for both the domains, we need to update the model properties - // file with the namespaces. For a cross-domain transaction to work, we need to have the externalDNSName - // set in the config file. Cannot set this after the domain is up since a server restart is - // required for this to take effect. So, copying the property file to RESULT_ROOT and updating the - // property file - updatePropertyFiles(); - createOnPremDomain(); - createOnPremDomainRoutingRules(); - modifyDNS(); - + //add DNS entries in the local machine + localAddress = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()); + modifyDNS(); + logger.info("installing WebLogic Deploy Tool"); + downloadAndInstallWDT(); // install and verify operator - installAndVerifyOperator(opNamespace, domain1Namespace); - createK8sDomain(); + installAndVerifyOperator(opNamespace, domain1Namespace, domain3Namespace); } /** * Stop on premise domain. */ - @AfterAll - public static void stopOnPremDomain() throws UnknownHostException { + @AfterEach + public void stopOnPremDomain() throws UnknownHostException, InterruptedException { shutdownServers(List.of(wlstScript.toString(), Path.of(RESOURCE_DIR, "onpremcrtx").toString() + "/shutdown.py", - getExternalDNSName()), + localAddress), Path.of(domainHome.toString(), "wlst.log")); + shutdownDomain(domainUT, domainNSUT); } /** @@ -188,8 +208,43 @@ public static void stopOnPremDomain() throws UnknownHostException { */ @Test @DisplayName("Check cross domain transcated MDB communication ") - void testCrossDomainTranscatedMDB() throws IOException, InterruptedException { + void testCrossDomainWithExternalJMSProvider() throws IOException, InterruptedException { + createOnPremDomainJMSProvider(); + //start on prem domain + startServers(domainHome); + + logger.info("creating routing rules for on prem domain to talk to JMS client in K8S cluster"); + Path srcRoutingFile = Path.of(RESOURCE_DIR, "onpremcrtx", ONPREM_DOMAIN_ROUTING); + String content = new String(Files.readAllBytes(srcRoutingFile), StandardCharsets.UTF_8); + Path ingressRoutingFile = File.createTempFile("ingressRoutingFile", ".yaml").toPath(); + Files.write(ingressRoutingFile, + content.replaceAll("NAMESPACE", domain1Namespace) + .replaceAll("traefik-onprem", onpremIngressClass) + .getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + execK8SCLI(ingressRoutingFile); + + String jmsProvider = "t3://" + localAddress + ":8002," + localAddress + ":8003"; + applicationsList = buildApplications(jmsProvider, null); + // build the model file list for domain1 + final List modelFilesListDomain1 = Arrays.asList( + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN1, + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS); + //create model property file list + Path propFile = File.createTempFile("k8s-jms-client", ".props").toPath(); + Files.writeString(propFile, + """ + ADMIN_USERNAME=weblogic + ADMIN_PASSWORD=welcome1 + DOMAIN_NAME=domain1 + CALCULATED_LISTENPORTS=false + NAMESPACE=""" + domain1Namespace, + StandardOpenOption.TRUNCATE_EXISTING); + List modelPropList = Collections.singletonList(propFile.toString()); + createK8sDomain(domainUid1, domain1Namespace, modelFilesListDomain1, modelPropList, applicationsList); + domainUT = domainUid1; + domainNSUT = domain1Namespace; + // No extra header info String curlHostHeader = ""; if (TestConstants.KIND_CLUSTER @@ -212,7 +267,7 @@ void testCrossDomainTranscatedMDB() throws IOException, InterruptedException { + "cf=jms.ClusterConnectionFactory&" + "action=send&" + "dest=jms/testCdtUniformTopic", - hostAndPort, getExternalDNSName(), port); + hostAndPort, localAddress, port); logger.info(url); HttpResponse response; @@ -222,13 +277,104 @@ void testCrossDomainTranscatedMDB() throws IOException, InterruptedException { "Can not send message to remote Distributed Topic"); } - assertTrue(checkLocalQueue(), + assertTrue(checkLocalQueue(hostAndPort), "Expected number of message not found in Accounting Queue"); } - private boolean checkLocalQueue() { + /** + * This test verifies cross-domain MessageDrivenBean communication A transacted MDB on Domain D1 listen on a + * replicated Distributed Topic on Domain D2. The MDB is deployed to a on premise domain D1 with + * MessagesDistributionMode set to One-Copy-Per-Server. The OnMessage() routine sends a message to local queue on + * receiving the message. An application servlet is deployed to Administration Server on D1 which send/receive message + * from a JMS destination based on a given URL. (a) app servlet send message to Distributed Topic on D2 (b) mdb puts a + * message into local Queue for each received message (c) make sure local Queue gets 2X times messages sent to + * Distributed Topic Since the MessagesDistributionMode is set to One-Copy-Per-Server and targeted to a cluster of two + * servers, onMessage() will be triggered for both instance of MDB for a message sent to Distributed Topic. Use WLSt + * script to get the messages count on the local queue populated by the MDB and verify it matches 20. + */ + @Test + @DisplayName("Check cross domain transcated MDB communication ") + void testCrossDomainWithK8SJMSProvider() throws IOException, InterruptedException { + + // build the model file list for k8s domain + final List k8sDomainModelFilesList = Arrays.asList( + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN3, + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS2); + + //create model property file list + Path propFile = File.createTempFile("k8s-jms-provider", ".props").toPath(); + Files.writeString(propFile, + """ + ADMIN_USERNAME=weblogic + ADMIN_PASSWORD=welcome1 + DOMAIN_NAME=domain3 + CALCULATED_LISTENPORTS=false + PUBLIC_LB_ADDRESS=""" + localAddress + + "\nPUBLIC_LB_PORT=" + IT_ONPREMCRDOMAINTX_CLUSTER_HOSTPORT, + StandardOpenOption.TRUNCATE_EXISTING); + List modelPropList = Collections.singletonList(propFile.toString()); + + logger.info("creating k8s domain hosting JMS provider in cluster with mode " + + "files {0} and property files {1}", k8sDomainModelFilesList, modelPropList); + createK8sDomain(domainUid3, domain3Namespace, k8sDomainModelFilesList, modelPropList, null); + domainUT = domainUid3; + domainNSUT = domain3Namespace; + + logger.info("creating node port service for K8S domain cluster-1"); + Path nodePortServiceFile = File.createTempFile(domainUid3, ".yaml").toPath(); + FileUtils.copy(Path.of(RESOURCE_DIR, "/onpremcrtx/", K8S_DOMAIN3_NODEPORT_ROUTING), nodePortServiceFile); + + FileUtils.replaceStringInFile(nodePortServiceFile.toString(), + "NAMESPACE", domain3Namespace); + FileUtils.replaceStringInFile(nodePortServiceFile.toString(), + "DOMAIN_NAME", domainUid3); + FileUtils.replaceStringInFile(nodePortServiceFile.toString(), + "NODE_PORT", String.valueOf(IT_ONPREMCRDOMAINTX_CLUSTER_NODEPORT)); + //create routing rules for on prem domain to communicate to K8S JMS provider + execK8SCLI(nodePortServiceFile); + + String jmsprovider = "t3://" + localAddress + ":" + IT_ONPREMCRDOMAINTX_CLUSTER_HOSTPORT; + logger.info("creating on prem domain, JMS clients for JMS providers running in K8S domain"); + createOnPremDomainJMSClient(jmsprovider); + + logger.info("starting on prem domain"); + startServers(domainHome); + + String hostAndPort = localAddress + ":" + adminServerPort; + assertTrue(checkAppIsActive(hostAndPort, "", "mdbtopic", "cluster-1", + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), + "MDB application can not be activated on domain1/cluster"); + + logger.info("MDB application is activated on domain1/cluster"); + + String url = String.format("http://%s/jmsservlet/jmstest?" + + "url=t3://%s:%s&" + + "cf=jms.ClusterConnectionFactory&" + + "action=send&" + + "dest=jms/testCdtUniformTopic", + hostAndPort, localAddress, IT_ONPREMCRDOMAINTX_CLUSTER_HOSTPORT); + logger.info(url); + + HttpResponse response1; + response1 = OracleHttpClient.get(url, null, true); + assertEquals(200, response1.statusCode(), "Didn't get the 200 HTTP status"); + assertTrue(response1.body().contains("Sent (10) message"), + "Can not send message to remote Distributed Topic"); + + testUntil(() -> { + executeWlst(List.of(wlstScript.toString(), + Path.of(RESOURCE_DIR, "onpremcrtx").toString() + "/getmessages.py", localAddress), + Path.of(domainHome.toString(), "accountingQueueMessages.log"), true); + String content = Files.readString(Path.of(domainHome.toString(), "accountingQueueMessages.log")); + logger.info(content); + return content.contains("messagesgot=20"); + }, logger, "checking for messages in testAccountingQueue to be equal to 20"); + } + + + private boolean checkLocalQueue(String hostAndPort) { String url = String.format("http://%s/jmsservlet/jmstest?" - + "url=t3://localhost:7001&" + + "url=t3://localhost:" + adminServerPort + "&" + "action=receive&dest=jms.testAccountingQueue", hostAndPort); @@ -250,69 +396,41 @@ private boolean checkLocalQueue() { return true; } + private static void createK8sDomain(String domainUid, String namespace, + List modelFilesList, List modelPropsList, List applicationsList) + throws UnknownHostException, IOException { - private static void updatePropertyFiles() { - //create a temporary directory to copy and update the properties file - Path target = Path.of(PROPS_TEMP_DIR); - Path source1 = Path.of(RESOURCE_DIR, "onpremcrtx", WDT_MODEL_DOMAIN1_PROPS); - Path source2 = Path.of(RESOURCE_DIR, "onpremcrtx", WDT_MODEL_DOMAIN2_PROPS); - Path source3 = Path.of(RESOURCE_DIR, "onpremcrtx", ONPREM_DOMAIN_ROUTING); - Path domain1Properties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN1_PROPS); - Path domain2Properties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN2_PROPS); - Path onPremDomainRouting = Path.of(PROPS_TEMP_DIR, ONPREM_DOMAIN_ROUTING); - logger.info("Copy the properties file to the above area so that we can add namespace property"); - assertDoesNotThrow(() -> { - Files.createDirectories(target); - Files.copy(source1, domain1Properties, StandardCopyOption.REPLACE_EXISTING); - Files.copy(source2, domain2Properties, StandardCopyOption.REPLACE_EXISTING); - //Files.copy(source3, onPremDomainRouting, StandardCopyOption.REPLACE_EXISTING); - Files.writeString(domain1Properties, "\nNAMESPACE=" + domain1Namespace, StandardOpenOption.APPEND); - Files.writeString(domain2Properties, "\nDNS_NAME=" + getExternalDNSName(), StandardOpenOption.APPEND); - String content = new String(Files.readAllBytes(source3), StandardCharsets.UTF_8); - Files.write(onPremDomainRouting, - content.replaceAll("NAMESPACE", domain1Namespace) - .replaceAll("traefik-onprem", onpremIngressClass) - .getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); - }); - } - - private static void createK8sDomain() throws UnknownHostException, IOException { - - List applicationsList = buildApplications(); - // create admin credential secret for domain1 - logger.info("Create admin credential secret for domain1"); - String domain1AdminSecretName = domainUid1 + "-weblogic-credentials"; + // create admin credential secret for domain + logger.info("Create admin credential secret for domain {0}", domainUid); + String domain1AdminSecretName = domainUid + "-weblogic-credentials"; assertDoesNotThrow(() -> createSecretWithUsernamePassword( - domain1AdminSecretName, domain1Namespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), - String.format("createSecret %s failed for %s", domain1AdminSecretName, domainUid1)); - - // build the model file list for domain1 - final List modelFilesListDomain1 = Arrays.asList( - RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN1, - RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS); + domain1AdminSecretName, namespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT), + String.format("createSecret %s failed for %s", domain1AdminSecretName, domainUid)); - logger.info("Creating image with model file and verify"); - String domain1Image = createImageAndVerify(WDT_IMAGE_NAME1, modelFilesListDomain1, - applicationsList, WDT_MODEL_DOMAIN1_PROPS, PROPS_TEMP_DIR, domainUid1); - logger.info("Created {0} image", domain1Image); + logger.info("Creating image with model files {0} and property files {1}", + modelFilesList, modelPropsList); + String domainCreationImage = createImageAndVerify( + WDT_IMAGE_NAME1, modelFilesList, applicationsList, + modelPropsList, WEBLOGIC_IMAGE_NAME, + WEBLOGIC_IMAGE_TAG, WLS, false, domainUid, false); + logger.info("Created {0} image", domainCreationImage); // repo login and push image to registry if necessary - imageRepoLoginAndPushImageToRegistry(domain1Image); + imageRepoLoginAndPushImageToRegistry(domainCreationImage); //create domain1 - createDomain(domainUid1, domain1Namespace, domain1AdminSecretName, domain1Image); - + createDomain(domainUid, namespace, domain1AdminSecretName, domainCreationImage); + if (TestConstants.KIND_CLUSTER && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { - hostHeader = createIngressHostRouting(domain1Namespace, domainUid1, adminServerName, 7001); - hostAndPort = formatIPv6Host(InetAddress.getLocalHost().getHostAddress()) - + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT; + hostHeader = createIngressHostRouting(namespace, domainUid, adminServerName, adminServerPort); + hostAndPort = localAddress + ":" + TRAEFIK_INGRESS_HTTP_HOSTPORT; headers = new HashMap<>(); headers.put("host", hostHeader); } } - private static List buildApplications() { + private static List buildApplications(String jmsProvider, String localServerAddress) throws IOException { //build jmsservlet client application archive Path targetDir; Path distDir; @@ -331,17 +449,23 @@ private static List buildApplications() { //build the MDB application Path mdbSrcDir = Paths.get(APP_DIR, "mdbtopic"); Path mdbDestDir = Paths.get(PROPS_TEMP_DIR, "mdbtopic"); + Files.createDirectories(Path.of(PROPS_TEMP_DIR)); assertDoesNotThrow(() -> copyFolder( mdbSrcDir.toString(), mdbDestDir.toString()), "Could not copy mdbtopic application directory"); Path template = Paths.get(PROPS_TEMP_DIR, "mdbtopic/src/application/MdbTopic.java"); - // Add the external ip addresses of the on-premise cluster instances - // so that it can communicate with remote destination on domain2 + // replace the JMS provider address to K8S or on prem domain depending on the use case assertDoesNotThrow(() -> replaceStringInFile( template.toString(), "t3://domain2-cluster-cluster-1.domain2-namespace:8001", - "t3://" + getExternalDNSName() + ":8002," + getExternalDNSName() + ":8003"), + jmsProvider), "Could not modify the provider url in MDB Template file"); + // this is needed when JMS provider is in K8S + if (null != localServerAddress) { + assertDoesNotThrow(() -> replaceStringInFile(template.toString(), "domain1-admin-server", + localServerAddress), + "Could not modify the local server url in MDB Template file"); + } //build application archive for MDB targetDir = Paths.get(WORK_DIR, ItOnPremCrossDomainTransaction.class.getName() + "/mdbtopic"); @@ -445,34 +569,96 @@ private static void createDomainResource(String domainUid, String domNamespace, + "for %s in namespace %s", domainUid, domNamespace)); } - private static void createOnPremDomain() throws IOException, InterruptedException { - logger.info("creating on premise domain"); - Path createDomainScript = downloadAndInstallWDT(); - mwHome = Path.of(RESULTS_ROOT, "mwhome"); - String modelFileList = RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN2 + "," + private static void createOnPremDomainJMSProvider() throws IOException, InterruptedException { + String onPremDomainName = "onpremJMSProdomain"; + logger.info("creating on prem domain model list"); + String modelFileList + = RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_DOMAIN2 + "," + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS2; - domainHome = Path.of(RESULTS_ROOT, "mwhome", "domains", "domain2"); + + //create model property file list + Path propFile = File.createTempFile("ext-jms-provider", ".props").toPath(); + Files.writeString(propFile, + """ + ADMIN_USERNAME=weblogic + ADMIN_PASSWORD=welcome1 + DOMAIN_NAME=domain2 + DNS_NAME=""" + localAddress, + StandardOpenOption.TRUNCATE_EXISTING); + + mwHome = Path.of(RESULTS_ROOT, "mwhome"); + domainHome = Path.of(RESULTS_ROOT, "mwhome", "domains", onPremDomainName); logger.info("creating on premise domain home {0}", domainHome); Files.createDirectories(domainHome); - Path modelProperties = Path.of(PROPS_TEMP_DIR, WDT_MODEL_DOMAIN2_PROPS); + + logger.info("creating on premise {0}", onPremDomainName); List command = List.of( createDomainScript.toString(), "-oracle_home", mwHome.toString(), "-domain_type", "WLS", "-domain_home", domainHome.toString(), "-model_file", modelFileList, - "-variable_file", modelProperties.toString() + "-variable_file", propFile.toString() ); runWDTandCreateDomain(command.stream().collect(Collectors.joining(" "))); createBootProperties(domainHome.toString()); - startServers(domainHome); wlstScript = Path.of(mwHome.toString(), "oracle_common", "common", "bin", "wlst.sh"); } + + private static void createOnPremDomainJMSClient(String jmsprovider) throws IOException, InterruptedException { + String onPremDomainName = "onpremJMSClidomain"; + + logger.info("build the applications to be deployed in onprem domain " + + "with JMS provider pointing to K8S domain and JMS client as local on prem domain"); + applicationsList = buildApplications(jmsprovider, InetAddress.getLocalHost().getHostAddress()); + Path buildAppArchiveZip = buildAppArchiveZip(applicationsList); + + logger.info("creating model property file for on prem domain {0}", onPremDomainName); + Path propFile = File.createTempFile(onPremDomainName, ".props").toPath(); + Files.writeString(propFile, + "DOMAIN_NAME=" + onPremDomainName + "\n" + + "ADMIN_USERNAME=weblogic\n" + + "CALCULATED_LISTENPORTS=true\n" + + "ADMIN_PASSWORD=welcome1\n", StandardOpenOption.TRUNCATE_EXISTING); + + logger.info("creating model file for on prem domain {0}", onPremDomainName); + Path modelFile = File.createTempFile(onPremDomainName, ".yaml").toPath(); + FileUtils.copy(Path.of(RESOURCE_DIR, "/onpremcrtx/", WDT_MODEL_FILE_DOMAIN1), modelFile); + //modify the model file to add proper external dns entries + FileUtils.replaceStringInFile(modelFile.toString(), + "@@PROP:DOMAIN_NAME@@-admin-server\\.@@PROP:NAMESPACE@@", localAddress); + FileUtils.replaceStringInFile(modelFile.toString(), + "@@PROP:DOMAIN_NAME@@-managed-server\\$\\{id\\}\\.@@PROP:NAMESPACE@@", localAddress); + + String modelFileList = modelFile.toString() + "," + + RESOURCE_DIR + "/onpremcrtx/" + WDT_MODEL_FILE_JMS; + + logger.info("creating on premise domain {0} with model files {1} and property file {2}", + onPremDomainName, modelFileList, propFile); + mwHome = Path.of(RESULTS_ROOT, "mwhome"); + domainHome = Path.of(RESULTS_ROOT, "mwhome", "domains", onPremDomainName); + logger.info("creating on premise domain home {0}", domainHome); + Files.createDirectories(domainHome); + + //call WDT create domain to create actual domain + List command = List.of( + createDomainScript.toString(), + "-oracle_home", mwHome.toString(), + "-domain_type", "WLS", + "-domain_home", domainHome.toString(), + "-archive_file", buildAppArchiveZip.toString(), + "-model_file", modelFileList, + "-variable_file", propFile.toString() + ); + runWDTandCreateDomain(command.stream().collect(Collectors.joining(" "))); + createBootProperties(domainHome.toString()); + wlstScript = Path.of(mwHome.toString(), "oracle_common", "common", "bin", "wlst.sh"); + } - private static Path downloadAndInstallWDT() throws IOException { + private static void downloadAndInstallWDT() throws IOException { String wdtUrl = WDT_DOWNLOAD_URL + "/download/weblogic-deploy.zip"; Path destLocation = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy.zip"); - Path createDomainScript = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy", "bin", "createDomain.sh"); + createDomainScript = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy", "bin", "createDomain.sh"); if (!Files.exists(destLocation) && !Files.exists(createDomainScript)) { logger.info("Downloading WDT to {0}", destLocation); Files.createDirectories(destLocation.getParent()); @@ -480,9 +666,7 @@ private static Path downloadAndInstallWDT() throws IOException { String cmd = "cd " + destLocation.getParent() + ";unzip " + destLocation; assertTrue(Command.withParams(new CommandParams().command(cmd)).execute(), "unzip command failed"); } - assertTrue(Files.exists(createDomainScript), "could not find createDomain.sh script"); - return createDomainScript; } private static void runWDTandCreateDomain(String command) { @@ -490,14 +674,11 @@ private static void runWDTandCreateDomain(String command) { assertTrue(Command.withParams(new CommandParams().command(command)).execute(), "create domain failed"); } - private static String getExternalDNSName() throws UnknownHostException { - return InetAddress.getLocalHost().getHostAddress(); - } - private static void createBootProperties(String domainHome) throws IOException { List servers = List.of("admin-server", "managed-server1", "managed-server2"); for (String server : servers) { Path securityDir = Files.createDirectories(Path.of(domainHome, "servers", server, "security")); + Files.deleteIfExists(Path.of(securityDir.toString(), "boot.properties")); Path bootFile = Files.createFile(Path.of(securityDir.toString(), "boot.properties")); logger.info("creating boot.properties {0}", bootFile); Files.writeString(bootFile, "username=weblogic\n", StandardOpenOption.TRUNCATE_EXISTING); @@ -512,11 +693,11 @@ private static void startServers(Path domainHome) throws InterruptedException, U TimeUnit.SECONDS.sleep(15); startWebLogicServer(List.of(domainHome.toString() + "/bin/startManagedWebLogic.sh", "managed-server1", - "t3://" + getExternalDNSName() + ":7001"), + "t3://" + localAddress + ":" + adminServerPort), Path.of(domainHome.toString(), "managed-server1.log")); startWebLogicServer(List.of(domainHome.toString() + "/bin/startManagedWebLogic.sh", "managed-server2", - "t3://" + getExternalDNSName() + ":7001"), + "t3://" + localAddress + ":" + adminServerPort), Path.of(domainHome.toString(), "managed-server2.log")); TimeUnit.SECONDS.sleep(15); } @@ -543,31 +724,18 @@ private static void startWebLogicServer(List command, Path logFile) { serverThread.start(); } - private static void shutdownServers(List command, Path logFile) { - Thread serverThread = new Thread(() -> { - ProcessBuilder processBuilder = new ProcessBuilder(command); - Map combinedEnvMap = new HashMap<>(); - combinedEnvMap.putAll(System.getenv()); - processBuilder.environment().putAll(combinedEnvMap); - processBuilder.redirectError(new File(logFile.toString())); - processBuilder.redirectOutput(new File(logFile.toString())); - try { - logger.info("shutting down servers using wlst " + String.join(" ", command)); - Process process = processBuilder.start(); - process.waitFor(); // This will wait for the process to complete in the thread - logger.info("Servers are shut down."); - } catch (IOException | InterruptedException e) { - logger.info(e.getLocalizedMessage()); - } - }); - serverThread.start(); + private static void shutdownServers(List command, Path logFile) throws InterruptedException { + logger.info("shutting down servers using wlst " + String.join(" ", command)); + executeWlst(command, logFile, false); } private static void modifyDNS() throws UnknownHostException, IOException, InterruptedException { - String dnsEntries = getExternalDNSName() + String managedServerPrefix = domainUid1 + "-managed-server"; + String dnsEntries = localAddress + " " + managedServerPrefix + "1." + domain1Namespace + " " + managedServerPrefix + "2." + domain1Namespace - + " " + domainUid1 + "-" + adminServerName + "." + domain1Namespace; + + " " + domainUid1 + "-" + adminServerName + "." + domain1Namespace + + " " + domainUid3 + "-managed-server1 " + domainUid3 + "-managed-server2"; String command = "echo \"" + dnsEntries + "\" | sudo tee -a /etc/hosts > /dev/null"; logger.info("adding DNS entries with command {0}", command); ExecResult result; @@ -577,8 +745,8 @@ private static void modifyDNS() throws UnknownHostException, IOException, Interr assertEquals(0, result.exitValue(), "adding DNS entries for on prem domain failed"); } - private static void createOnPremDomainRoutingRules() throws IOException, InterruptedException { - String command = KUBERNETES_CLI + " apply -f " + Path.of(PROPS_TEMP_DIR, ONPREM_DOMAIN_ROUTING); + private static void execK8SCLI(Path routingFile) throws IOException, InterruptedException { + String command = KUBERNETES_CLI + " apply -f " + routingFile; logger.info("creating ingress routing rules for onprem domain \n{0}", command); ExecResult result; result = exec(command, true); @@ -593,4 +761,49 @@ private static String installTraefikForPremDomain() { IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT, 0).getIngressClassName(); } + private static Path buildAppArchiveZip(List archiveAppsList) { + AppParams appParams = defaultAppParams() + .appArchiveDir(ARCHIVE_DIR + generateRandomString()).srcDirList(archiveAppsList); + assertTrue(archiveApp(appParams)); + return Path.of(appParams.appArchiveDir(), appParams.appName() + ".zip"); + } + + public static String generateRandomString() { + int length = 10; // Desired length of the random string + String characterSet = Charset.forName("US-ASCII").toString(); + Random random = new Random(); + StringBuilder sb = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + sb.append(characterSet.charAt(random.nextInt(characterSet.length()))); + } + return sb.toString(); + } + + private static void executeWlst(List command, Path logFile, boolean wait) throws InterruptedException { + Thread serverThread = new Thread(() -> { + ProcessBuilder processBuilder = new ProcessBuilder(command); + Map combinedEnvMap = new HashMap<>(); + combinedEnvMap.putAll(System.getenv()); + processBuilder.environment().putAll(combinedEnvMap); + processBuilder.redirectError(new File(logFile.toString())); + processBuilder.redirectOutput(new File(logFile.toString())); + try { + logger.info("running wlst script" + String.join(" ", command)); + Process process = processBuilder.start(); + process.waitFor(); // This will wait for the process to complete in the thread + logger.info("finished running wlst script."); + } catch (IOException | InterruptedException e) { + logger.info(e.getLocalizedMessage()); + } + }); + serverThread.start(); + if (wait) { + while (serverThread.isAlive()) { + logger.info("waiting for the wlst script to finish executing"); + TimeUnit.SECONDS.sleep(5); + } + } + } + } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index c044618cff5..51f8fa86c3b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -571,5 +571,7 @@ public interface TestConstants { public static final int IT_ONPREMCRDOMAINTX_INGRESS_HTTP_NODEPORT = 31132; public static final int IT_ONPREMCRDOMAINTX_INGRESS_HTTP_HOSTPORT = 8001; + public static final int IT_ONPREMCRDOMAINTX_CLUSTER_NODEPORT = 31136; + public static final int IT_ONPREMCRDOMAINTX_CLUSTER_HOSTPORT = 2232; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/AppBuilder.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/AppBuilder.java index 7b047e5c72d..0dd8bf0175f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/AppBuilder.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/AppBuilder.java @@ -244,6 +244,7 @@ public boolean archiveApp() { List srcFiles = params.srcDirList(); String srcFile = srcFiles.get(0); String appName = srcFile.substring(srcFile.lastIndexOf("/") + 1, srcFile.lastIndexOf(".")); + params.appName(appName); String archiveSrcDir = params.appArchiveDir() + "/wlsdeploy/applications"; try { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java index d5b85cb1d47..b726c5604cf 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/FileUtils.java @@ -496,8 +496,8 @@ public static void replaceStringInFile(String filePath, String regex, String rep Path src = Paths.get(filePath); logger.info("Replacing {0} in {1} with {2}", regex, src.toString(), replacement); String content = new String(Files.readAllBytes(src), StandardCharsets.UTF_8); - if (!content.contains(regex)) { - logger.info("search string {0} not found to replace with {1}", regex, replacement); + if (!content.matches(regex)) { + logger.warning("search string {0} not found to replace with {1}", regex, replacement); } long oldModified = src.toFile().lastModified(); Files.write(src, content.replaceAll(regex, replacement).getBytes(StandardCharsets.UTF_8)); diff --git a/integration-tests/src/test/resources/apps/mdbtopic/src/application/MdbTopic.java b/integration-tests/src/test/resources/apps/mdbtopic/src/application/MdbTopic.java index cbb1001e6c7..f7b0afb08d6 100644 --- a/integration-tests/src/test/resources/apps/mdbtopic/src/application/MdbTopic.java +++ b/integration-tests/src/test/resources/apps/mdbtopic/src/application/MdbTopic.java @@ -79,7 +79,9 @@ public void onMessage (Message msg) { ConnectionFactory cf=(ConnectionFactory)cxt.lookup("weblogic.jms.ConnectionFactory"); + System.out.println(" weblogic.jms.ConnectionFactory lookup successful in local domain"); Destination rq=(Destination)cxt.lookup("jms.testAccountingQueue"); + System.out.println(" jms.testAccountingQueue lookup successful in local domain"); JMSContext context = cf.createContext(); tmsg = context.createTextMessage("(On Server) " + serverRuntime.getName()); context.createProducer().send(rq,tmsg); diff --git a/integration-tests/src/test/resources/onpremcrtx/getmessages.py b/integration-tests/src/test/resources/onpremcrtx/getmessages.py new file mode 100644 index 00000000000..6b36e21d72a --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/getmessages.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +hostname=sys.argv[1] +connect('weblogic','welcome1','t3://'+hostname+':7001') +serverRuntime() +cd('JMSRuntime/admin-server.jms/JMSServers/TestAdminJmsServer/Destinations/TestAdminJmsModule!testAccountingQueue') +ls() +count=cmo.getMessagesCurrentCount() +print 'messagesgot='+str(count) +disconnect() +exit() + diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties index 758dccc32fa..50f9bc75999 100644 --- a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.properties @@ -4,4 +4,3 @@ DOMAIN_NAME=domain1 ADMIN_USERNAME=weblogic ADMIN_PASSWORD=welcome1 - diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml index f8dd69a5831..1ec885abb43 100644 --- a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain1.yaml @@ -16,7 +16,7 @@ topology: ServerNamePrefix: "managed-server" DynamicClusterSize: 2 MaxDynamicClusterSize: 2 - CalculatedListenPorts: false + CalculatedListenPorts: '@@PROP:CALCULATED_LISTENPORTS@@' Server: "admin-server": ListenPort: 7001 diff --git a/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain3.yaml b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain3.yaml new file mode 100644 index 00000000000..6922aadefdf --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/model-crossdomaintransaction-domain3.yaml @@ -0,0 +1,38 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +domainInfo: + AdminUserName: '@@PROP:ADMIN_USERNAME@@' + AdminPassword: '@@PROP:ADMIN_PASSWORD@@' + ServerStartMode: 'prod' + +topology: + Name: '@@PROP:DOMAIN_NAME@@' + AdminServerName: "admin-server" + Cluster: + "cluster-1": + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "managed-server" + DynamicClusterSize: 2 + MaxDynamicClusterSize: 2 + CalculatedListenPorts: '@@PROP:CALCULATED_LISTENPORTS@@' + Server: + "admin-server": + ListenPort: 7001 + ServerTemplate: + "cluster-1-template": + Cluster: "cluster-1" + ListenPort : 9001 + NetworkAccessPoint: + MyT3Channel: + Protocol: 't3' + ListenPort: 7999 + PublicPort: '@@PROP:PUBLIC_LB_PORT@@' + PublicAddress: '@@PROP:PUBLIC_LB_ADDRESS@@' + HttpEnabledForThisProtocol: true + TunnelingEnabled: true + OutboundEnabled: false + Enabled: true + TwoWaySSLEnabled: false + ClientCertificateEnforced: false diff --git a/integration-tests/src/test/resources/onpremcrtx/onprem-domain2-node-portrouting.yaml b/integration-tests/src/test/resources/onpremcrtx/onprem-domain2-node-portrouting.yaml new file mode 100644 index 00000000000..ae3ee9de1a4 --- /dev/null +++ b/integration-tests/src/test/resources/onpremcrtx/onprem-domain2-node-portrouting.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2024, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +apiVersion: v1 +kind: Service +metadata: + namespace: NAMESPACE + name: DOMAIN_NAME-cluster-cluster-1-ext + labels: + weblogic.domainUID: DOMAIN_NAME +spec: + type: NodePort + externalTrafficPolicy: Cluster + sessionAffinity: ClientIP + selector: + weblogic.domainUID: DOMAIN_NAME + weblogic.clusterName: cluster-1 + ports: + - name: myclustert1channel + nodePort: NODE_PORT + port: 7999 + protocol: TCP + targetPort: 7999 From 12544fb1f5721306ec1233130fa71445fad29b14 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 17 Dec 2024 14:02:39 -0500 Subject: [PATCH 246/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 38fc2c5b56b..52061f7a8ef 100644 --- a/pom.xml +++ b/pom.xml @@ -688,12 +688,12 @@ 3.5.2 3.6.0 3.1.1 - 3.11.1 + 3.11.2 3.5.2 3.8.1 3.6.0 3.5.0 - 10.20.1 + 10.21.0 1.0 3.6.0 3.2.7 @@ -732,10 +732,10 @@ 6.1.0 0.16.0 2.18.2 - 2.18.1 + 2.18.2 2.3 2.11.0 - 11.1.0 + 11.1.1 2.0.16 1.5.12 4.29.1 From 17caee0357b2b282f0de42b34a4d1659675da3d0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 17 Dec 2024 15:17:42 -0500 Subject: [PATCH 247/356] Dependency updates --- operator/pom.xml | 5 ----- pom.xml | 7 +------ 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/operator/pom.xml b/operator/pom.xml index 4b067d802f2..f552b59c42e 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -425,11 +425,6 @@ kotlin-stdlib - - org.jetbrains.kotlin - kotlin-stdlib-common - - com.squareup.okhttp3 okhttp diff --git a/pom.xml b/pom.xml index 52061f7a8ef..6daf5816afb 100644 --- a/pom.xml +++ b/pom.xml @@ -430,11 +430,6 @@ kotlin-stdlib ${kotlin-stdlib-version} - - org.jetbrains.kotlin - kotlin-stdlib-common - ${kotlin-stdlib-version} - com.squareup.okhttp3 okhttp @@ -717,7 +712,7 @@ 4.2.2 19.0.1 3.0.1u2 - 2.0.21 + 2.1.0 4.12.0 3.9.1 1.79 From 6dd301ecf819663c0cbbab45465b03e8650013a0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 18 Dec 2024 13:32:06 -0500 Subject: [PATCH 248/356] Use WME 2.2.2 --- documentation/domains/Domain.json | 4 ++-- documentation/domains/Domain.md | 2 +- kubernetes/crd/domain-crd.yaml | 6 +++--- .../oracle/kubernetes/operator/KubernetesConstants.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index d1d922c0b42..c5ac2c2d532 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -880,8 +880,8 @@ "type": "object", "properties": { "image": { - "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1", - "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1", + "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2", + "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2", "type": "string" }, "imagePullPolicy": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 2ba3641c9c3..fe5f5a87c56 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -145,7 +145,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | Name | Type | Description | | --- | --- | --- | | `configuration` | Map | The configuration for the WebLogic Monitoring Exporter. If WebLogic Server instances are already running and have the monitoring exporter sidecar container, then changes to this field will be propagated to the exporter without requiring the restart of the WebLogic Server instances. | -| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1 | +| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2 | | `imagePullPolicy` | string | The image pull policy for the WebLogic Monitoring Exporter sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. | | `port` | integer | The port exposed by the WebLogic Monitoring Exporter running in the sidecar container. Defaults to 8080. The port value must not conflict with a port used by any WebLogic Server instance, including the ports of built-in channels or network access points (NAPs). | | `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. | diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index fc45002df75..b822f178f3b 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 8cd5a2176fe99b104c82048d750d42f1130341bdfdba825493bc64de45025424 + weblogic.sha256: 7a1a4cb0613c9b7b8e8478d852cf4b239f718496ff389d02317c00e49cffd6ec name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -47,9 +47,9 @@ spec: appropriate. See https://github.com/oracle/weblogic-monitoring-exporter. properties: image: - default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1 + default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2 description: The WebLogic Monitoring Exporter sidecar container - image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1 + image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2 type: string imagePullPolicy: description: The image pull policy for the WebLogic Monitoring diff --git a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java index 8668ad09c2d..d61821e5af7 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java @@ -8,7 +8,7 @@ /** Kubernetes constants. */ public interface KubernetesConstants { String DEFAULT_IMAGE = "container-registry.oracle.com/middleware/weblogic:12.2.1.4"; - String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.1"; + String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2"; String DEFAULT_FLUENTD_IMAGE = "fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2"; String EXPORTER_CONTAINER_NAME = "monitoring-exporter"; String LATEST_IMAGE_SUFFIX = ":latest"; From 0bdc1c6ec73c5fb058cc533f88c9a7978099f4bd Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 18 Dec 2024 15:53:13 -0500 Subject: [PATCH 249/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6daf5816afb..adbf3b2825c 100644 --- a/pom.xml +++ b/pom.xml @@ -716,7 +716,7 @@ 4.12.0 3.9.1 1.79 - 5.11.3 + 5.11.4 5.7.1 1.7.0 1.3.2 From b5580105405653369c9f5e4eb3cae68d225d419f Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 19 Dec 2024 11:36:59 -0500 Subject: [PATCH 250/356] Correct calculation of latest version label --- .github/workflows/publish-latest-minor-gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-latest-minor-gh-pages.yml b/.github/workflows/publish-latest-minor-gh-pages.yml index 600627e4326..210f6b69681 100644 --- a/.github/workflows/publish-latest-minor-gh-pages.yml +++ b/.github/workflows/publish-latest-minor-gh-pages.yml @@ -47,7 +47,7 @@ jobs: cd $GITHUB_WORKSPACE/branch/documentation echo "Documentation branch is $GITHUB_REF_NAME..." - latest_tag=$(git ls-remote https://github.com/oracle/weblogic-kubernetes-operator.git --h --sort origin "refs/tags/v*" | cut -f2 | grep v4.2 | tail -1 | cut -c12-) + latest_tag=$(git ls-remote https://github.com/oracle/weblogic-kubernetes-operator.git --h --sort origin "refs/tags/v*" | cut -f2 | grep v4.2 | cut -c12- | sort --version-sort | tail -1) echo "Latest tag is $latest_tag..." echo $latest_tag >| $GITHUB_WORKSPACE/branch/documentation/site/layouts/shortcodes/latestVersion.html cat $GITHUB_WORKSPACE/branch/documentation/site/layouts/shortcodes/latestVersion.html From 3be63c58d068032a9f59b2f34386afeda3961f87 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 19 Dec 2024 14:13:07 -0500 Subject: [PATCH 251/356] Add mention of Kubernetes 1.31.1 --- .../site/content/introduction/prerequisites/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/introduction/prerequisites/introduction.md b/documentation/site/content/introduction/prerequisites/introduction.md index 792b080e71d..b2543ae3ac9 100644 --- a/documentation/site/content/introduction/prerequisites/introduction.md +++ b/documentation/site/content/introduction/prerequisites/introduction.md @@ -7,7 +7,7 @@ weight: 5 For the current production release {{< latestVersion >}}: -* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, 1.28.2+, 1.29.1+, and 1.30.1+ (check with `kubectl version`). +* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, 1.28.2+, 1.29.1+, 1.30.1+, and 1.31.1+ (check with `kubectl version`). * Flannel networking v0.13.0-amd64 or later (check with `docker images | grep flannel`), Calico networking v3.16.1 or later, *or* OpenShift SDN on OpenShift 4.3 systems. * Docker 19.03.1+ (check with `docker version`) *or* CRI-O 1.20.2+ (check with `crictl version | grep RuntimeVersion`). From bba949519e09d19278318d98e1655512efb3ff90 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 19 Dec 2024 14:21:26 -0500 Subject: [PATCH 252/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index adbf3b2825c..b00fbd46a78 100644 --- a/pom.xml +++ b/pom.xml @@ -732,8 +732,8 @@ 2.11.0 11.1.1 2.0.16 - 1.5.12 - 4.29.1 + 1.5.13 + 4.29.2 2.5.1 9.47 ${project.basedir}/src-generated-swagger From 2036acd75d16c5ad5cd2d3816d193b7c20a87eab Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 19 Dec 2024 15:24:29 -0500 Subject: [PATCH 253/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b00fbd46a78..a3ac083e893 100644 --- a/pom.xml +++ b/pom.xml @@ -732,7 +732,7 @@ 2.11.0 11.1.1 2.0.16 - 1.5.13 + 1.5.14 4.29.2 2.5.1 9.47 From b235451abe9d3b06bdf66a84eb8e197b3f49a5ea Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 20 Dec 2024 14:23:16 +0000 Subject: [PATCH 254/356] Merge branch 'owls-120737-doc' into 'main' Documentation to clarify dedicated mode See merge request weblogic-cloud/weblogic-kubernetes-operator!4896 (cherry picked from commit b4be284cefe62b24372f76818b8ae6767163c7bd) 01920541 Documentation to clarify dedicated mode f08db12f Correct for clarity --- .../namespace-management.md | 12 +- .../content/managing-operators/preparation.md | 124 +++++++++++------- 2 files changed, 84 insertions(+), 52 deletions(-) diff --git a/documentation/site/content/managing-operators/namespace-management.md b/documentation/site/content/managing-operators/namespace-management.md index 0d062d6db29..1759ca589eb 100644 --- a/documentation/site/content/managing-operators/namespace-management.md +++ b/documentation/site/content/managing-operators/namespace-management.md @@ -47,12 +47,12 @@ The operator installation Helm chart [domainNamespaceSelectionStrategy]({{}}) configuration setting controls which namespaces an operator manages. -|Strategy|Description|Example| -|-|-|-| -|`LabelSelector`|This is the default. The operator will manage namespaces with Kubernetes labels that match the label selector defined by your Helm chart configuration `domainNamespaceLabelSelector` attribute, which defaults to `weblogic-operator=enabled`.|With the Helm chart defaults, the operator will manage namespaces that have a label named `weblogic-operator` when this label has the value `enabled`. You can label the namespace `sample-domain1-ns` using the command `kubectl label namespace sample-domain1-ns weblogic-operator=enabled`. You can define a different label selector for your operator installation using the `domainNamespaceLabelSelector`, such as `--set "domainNamespaceLabelSelector=environment\=prod"`. For detailed syntax requirements, see [domainNamespaceLabelSelector]({{}}) in the Configuration Reference. | -|`RegExp`|The operator will manage namespaces that match the regular expression set by your `domainNamespaceRegExp` Helm chart configuration attribute.|If you want an operator to manage namespaces that start with the string `prod`, then use `--set "domainNamespaceSelectionStrategy=RegExp"` for your operator Helm configuration setting, and set the `domainNamespaceRegExp` Helm chart configuration attribute using `--set "domainNamespaceRegExp=^prod"`.| -|`Dedicated`|The operator will manage only domains that are in the same namespace as the operator.|If the operator is deployed to namespace `my-operator-ns` and was installed using `--set "domainNamespaceSelectionStrategy=Dedicated"` in its Helm chart configuration, then the operator will manage only domains in the `my-operator-ns` namespace.| -|`List`|The operator will manage the namespaces included in the `domainNamespaces` operator installation Helm chart configuration value, which is a list that defaults to `{default}`.|If you want to manage namespaces `default` and `ns1`, then in your operator installation Helm chart configuration, use `--set "domainNamespaceSelectionStrategy=List"` and `--set "domainNamespaces={default,ns1}"`.| +| Strategy | Description | Example | +|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `LabelSelector` | This is the default. The operator will manage namespaces with Kubernetes labels that match the label selector defined by your Helm chart configuration `domainNamespaceLabelSelector` attribute, which defaults to `weblogic-operator=enabled`. | With the Helm chart defaults, the operator will manage namespaces that have a label named `weblogic-operator` when this label has the value `enabled`. You can label the namespace `sample-domain1-ns` using the command `kubectl label namespace sample-domain1-ns weblogic-operator=enabled`. You can define a different label selector for your operator installation using the `domainNamespaceLabelSelector`, such as `--set "domainNamespaceLabelSelector=environment\=prod"`. For detailed syntax requirements, see [domainNamespaceLabelSelector]({{}}) in the Configuration Reference. | +| `RegExp` | The operator will manage namespaces that match the regular expression set by your `domainNamespaceRegExp` Helm chart configuration attribute. | If you want an operator to manage namespaces that start with the string `prod`, then use `--set "domainNamespaceSelectionStrategy=RegExp"` for your operator Helm configuration setting, and set the `domainNamespaceRegExp` Helm chart configuration attribute using `--set "domainNamespaceRegExp=^prod"`. | +| `Dedicated` | The operator will manage only domains that are in the same namespace as the operator. | If the operator is deployed to namespace `my-operator-ns` and was installed using `--set "domainNamespaceSelectionStrategy=Dedicated"` in its Helm chart configuration, then the operator will manage only domains in the `my-operator-ns` namespace. Customers often choose this strategy if a third-party or infrastructure team manages the Kubernetes cluster and the team installing the operator, such as an applications team, will have privilege only within one namespace. For more details on this use case, see [Local namespace only with cluster role binding disabled]({{}}). | +| `List` | The operator will manage the namespaces included in the `domainNamespaces` operator installation Helm chart configuration value, which is a list that defaults to `{default}`. | If you want to manage namespaces `default` and `ns1`, then in your operator installation Helm chart configuration, use `--set "domainNamespaceSelectionStrategy=List"` and `--set "domainNamespaces={default,ns1}"`. | For detailed reference information about each setting, see [WebLogic domain management]({{}}). diff --git a/documentation/site/content/managing-operators/preparation.md b/documentation/site/content/managing-operators/preparation.md index a401bc9bf99..5cc3f70aa13 100644 --- a/documentation/site/content/managing-operators/preparation.md +++ b/documentation/site/content/managing-operators/preparation.md @@ -31,7 +31,7 @@ Before installing an operator, ensure that each of these prerequisite requiremen 1. [Be aware of advanced operator configuration options](#be-aware-of-advanced-operator-configuration-options) 1. Special use cases: - [How to download the Helm chart if Internet access is not available](#how-to-download-the-helm-chart-if-internet-access-is-not-available) - - [How to manually install the Domain resource custom resource definition (CRD)](#how-to-manually-install-the-domain-resource-custom-resource-definition-crd) + - [How to manually install the Domain and Cluster custom resource definitions (CRD)](#how-to-manually-install-the-domain-and-cluster-custom-resource-definitions-crd) #### Check environment @@ -61,7 +61,7 @@ Before installing an operator, ensure that each of these prerequisite requiremen Before installing an operator, the operator Helm chart must be made available. The operator Helm chart includes: - Pre-configured default values for the configuration of the operator. -- Helm configuration value settings for fine tuning operator behavior. +- Helm configuration value settings for fine-tuning operator behavior. - Commands for deploying (installing) or undeploying the operator. You can set up access to the operator Helm chart using the chart repository. @@ -73,11 +73,11 @@ You can set up access to the operator Helm chart using the chart repository. `https://oracle.github.io/weblogic-kubernetes-operator/charts` repository and name the repository reference `weblogic-operator`, use the following command, `helm repo add `: - ```text + ```shell $ helm repo add weblogic-operator https://oracle.github.io/weblogic-kubernetes-operator/charts --force-update ``` - To verify that a Helm chart repository was added correctly, or to list existing repositories: - ```text + ```shell $ helm repo list ``` ```text @@ -91,7 +91,7 @@ You can set up access to the operator Helm chart using the chart repository. - To list the versions of the operator that you can install from the Helm chart repository: - ```text + ```shell $ helm search repo weblogic-operator/weblogic-operator --versions ``` @@ -103,7 +103,7 @@ You can set up access to the operator Helm chart using the chart repository. You can find out the configuration values that the operator Helm chart supports, as well as the default values, using the `helm show` command. - ```text + ```shell $ helm show values weblogic-operator/weblogic-operator ``` @@ -145,11 +145,11 @@ To simplifies management and monitoring of an operator, Oracle recommends: Here's an example of each: -```text +```shell $ kubectl create namespace sample-weblogic-operator-ns ``` -```text +```shell $ kubectl create serviceaccount -n sample-weblogic-operator-ns sample-weblogic-operator-sa ``` @@ -199,7 +199,7 @@ If you want to manually place an operator image in a particular machine's container image pool, or test access to an image, then call `docker pull`. For example: -```text +```shell $ docker pull ghcr.io/oracle/weblogic-kubernetes-operator:{{< latestVersion >}} ``` @@ -282,24 +282,47 @@ that matches its namespace selection criteria until you upgrade the operator's Helm release. See [Ensuring the operator has permission to manage a namespace]({{< relref "/managing-operators/namespace-management#ensuring-the-operator-has-permission-to-manage-a-namespace" >}}). -**NOTE**: You will need to manually install the operator CRD +**NOTE**: You will need to manually install the Domain and Cluster CRDs because `enableClusterRoleBinding` is not set to `true` and installation of the CRD requires cluster role binding privileges. -See [How to manually install the Domain resource custom resource definition (CRD)](#how-to-manually-install-the-domain-resource-custom-resource-definition-crd). +See [How to manually install the Domain and Cluster custom resource definitions (CRD)](#how-to-manually-install-the-domain-and-cluster-custom-resource-definitions-crd). ##### Local namespace only with cluster role binding disabled If you want to limit the operator so that it can access only resources in its local namespace, then: -- Choose a `Dedicated` namespace selection strategy. +- Choose the `Dedicated` namespace selection strategy. See [Choose a domain namespace selection strategy](#choose-a-domain-namespace-selection-strategy). -- You will need to manually install the operator CRD +- Install only the operator deployment and omit installing the webhook deployment using `operatorOnly=true`. This is because the webhook deployment must modify the Domain CRD to register the schema conversion webhook endpoint or register the validating webhook endpoint, both of which involve cluster-level resources. +- You will need to manually install the Domain and Cluster CRDs because `enableClusterRoleBinding` is not set to `true` and installing the CRD requires cluster role binding privileges. - See [How to manually install the Domain resource custom resource definition (CRD)](#how-to-manually-install-the-domain-resource-custom-resource-definition-crd). + See [How to manually install the Domain and Cluster custom resource definitions (CRD)](#how-to-manually-install-the-domain-and-cluster-custom-resource-definitions-crd). This may be necessary in environments where the operator cannot have cluster-scoped privileges, -such as may happen on OpenShift platforms when running the operator with a `Dedicated` namespace strategy. +such as may happen on OpenShift platforms or when running the operator with a `Dedicated` namespace strategy. + +Many customers do not have administrative privileges to their Kubernetes cluster because either a third-party or an infrastructure team is responsible for managing the cluster. In these cases, the customer, such as an applications team, will only have privilege in a single namespace. +As described above, the CRD documents must be installed in advance if the operator will not have sufficient privilege at the Kubernetes cluster-level to manage the lifecycle of these CRD documents. Therefore, the third-party or infrastructure team must complete the `kubectl create` of the CRD documents prior to the application team's installation of the operator. + +At a minimum, the infrastructure team must install the CRD documents and create the namespace for the operator: + +```shell +$ kubectl create -f https://raw.githubusercontent.com/oracle/weblogic-kubernetes-operator/refs/heads/release/4.2/kubernetes/crd/domain-crd.yaml +$ kubectl create -f https://raw.githubusercontent.com/oracle/weblogic-kubernetes-operator/refs/heads/release/4.2/kubernetes/crd/cluster-crd.yaml +$ kubectl create ns weblogic-operator +``` + +This would then allow the applications team to install a namespace-dedicated version of the operator without any webhooks or other cluster-level resources: + +```shell +helm repo add weblogic-operator https://oracle.github.io/weblogic-kubernetes-operator/charts +helm install weblogic-operator weblogic-operator/weblogic-operator --namespace weblogic-operator --set enableClusterRoleBinding=false --set domainNamespaceSelectionStrategy=Dedicated --set operatorOnly=true +``` + +{{% notice note %}} +Since this combination of options omits installing the webhook deployment, customers must use the `v9` schema version for Domain resources and manually uprade any `v8` resources from the 3.x version of the operator. +{{% /notice %}} #### Choose a domain namespace selection strategy @@ -350,13 +373,13 @@ so that then you can run `helm install` to install the operator. The steps are: 1. On a machine with Internet access, to download the chart to the current directory, run: -```text +```shell $ helm pull weblogic-operator --repo https://oracle.github.io/weblogic-kubernetes-operator/charts --destination . ``` For a specified version of the Helm chart, with `helm pull` and `helm install`, use the `--version ` option to choose the version that you want, with the `latest` value being the default. To list the versions of the operator that you can install from the Helm chart repository, run: -```text +```shell $ helm repo add weblogic-operator https://oracle.github.io/weblogic-kubernetes-operator/charts --force-update $ helm search repo weblogic-operator/weblogic-operator --versions ``` @@ -371,55 +394,64 @@ Be sure to follow all the previously detailed prerequisite steps, ending at Step 5. To install the operator, run `$ helm install weblogic-operator ./weblogic-operator --namespace weblogic-operator`. -#### How to manually install the Domain resource custom resource definition (CRD) +#### How to manually install the Domain and Cluster custom resource definitions (CRD) -The Domain resource type is defined by a Kubernetes CustomResourceDefinition (CRD) resource. -The domain CRD provides Kubernetes with the schema for operator domain resources -and there must be one domain CRD installed in each Kubernetes cluster that hosts the operator. +The Domain and Cluster resource types are defined by Kubernetes CustomResourceDefinition (CRD) resources. +The Domain and Cluster CRDs provide Kubernetes with the schema for WebLogic-related resources +and these two CRDs must be installed in each Kubernetes cluster that hosts the operator. If you install multiple operators in the same Kubernetes cluster, then they all -share the same domain CRD. +share the same CRDs. -**When does a Domain CRD need to be manually installed?** +**When do the Domain and Cluster CRDs need to be manually installed?** -Typically, the operator automatically installs the CRD for the Domain type when the operator first starts. -However, if the operator lacks sufficient permission to install it, -then you may choose to manually install the CRD in advance by using one of the provided YAML files. -Manually installing the CRD in advance allows you to run the operator without giving it privilege -(through Kubernetes roles and bindings) to access or update the CRD or other cluster-scoped resources. +Typically, the operator's webhook deployment automatically installs the CRDs when it first starts. +However, if the webhook lacks sufficient permission to install the CRDs, +then you must choose to manually install the CRD documents in advance by using the provided YAML files. +Manually installing the CRDs in advance allows you to run the operator without giving it privilege +(through Kubernetes roles and bindings) to access or update the CRD documents or other cluster-scoped resources. This may be necessary in environments where the operator cannot have cluster-scoped security privileges, -such as on OpenShift when running the operator with a `Dedicated` namespace strategy. +such when running the operator with a `Dedicated` namespace strategy. See [Choose a security strategy](#choose-a-security-strategy). -**How to manually install a Domain CRD.** +**How to manually install the Domain and Cluster CRDs.** -To manually install the CRD, first [download the operator source]({{< relref "/developerguide/requirements#obtaining-the-operator-source-code" >}}). +To manually install the CRDs, perform the following steps: -Assuming you have installed the operator source into the `/tmp` directory: - -```text -$ cd /tmp/weblogic-kubernetes-operator -``` - -```text -$ kubectl create -f kubernetes/crd/domain-crd.yaml +```shell +$ kubectl create -f https://raw.githubusercontent.com/oracle/weblogic-kubernetes-operator/refs/heads/release/4.2/kubernetes/crd/domain-crd.yaml +$ kubectl create -f https://raw.githubusercontent.com/oracle/weblogic-kubernetes-operator/refs/heads/release/4.2/kubernetes/crd/cluster-crd.yaml ``` -**How to check if a Domain CRD has been installed.** +**How to check if a Domain and Cluster CRDs have been installed.** -After the CustomResourceDefinition is installed, -either by the operator or using the previous `create` command, -you can verify that the CRD is installed correctly using: +You can verify that the Domain CRD is installed correctly using: -```text +```shell $ kubectl get crd domains.weblogic.oracle ``` Or, by calling: -```text +```shell $ kubectl explain domain.spec ``` The `kubectl explain` call should succeed and list the domain resource's `domain.spec` attributes -that are defined in the CRD. +that are defined in the Domain CRD. + +Similarly, you can verify that the Cluster CRD is installed correctly using: + +```shell +$ kubectl get crd clusters.weblogic.oracle +``` + +Or, by calling: + +```shell +$ kubectl explain cluster.spec +``` + +The `kubectl explain` call should succeed +and list the cluster resource's `cluster.spec` attributes +that are defined in the Cluster CRD. From a870d33b4278e912feb96ba2df835722a44d605a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 20 Dec 2024 09:28:20 -0500 Subject: [PATCH 255/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a3ac083e893..2c90cc22266 100644 --- a/pom.xml +++ b/pom.xml @@ -707,7 +707,7 @@ 0.9.6 3.6.0 1.0.0 - 3.26.3 + 3.27.0 2.18.0 4.2.2 19.0.1 From 6d5e94c0736257b48523d8c80bafcc144de7accc Mon Sep 17 00:00:00 2001 From: rosemary_marano Date: Fri, 20 Dec 2024 17:56:12 +0000 Subject: [PATCH 256/356] Update references to WLS docs --- .../major-weblogic-version-upgrade/upgrade-14210.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md index 902f0716503..acbb37245d3 100644 --- a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md +++ b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md @@ -26,13 +26,13 @@ Some important secured production mode changes are: * Demo SSL certificates have been changed completely from previous releases; there are special considerations when using them. For more information, see [Using demo SSL certificates in v14.1.2.0.0 or later]({{< relref "/managing-domains/model-in-image/overview#using-demo-ssl-certificates-in-v141200-or-later" >}}). -For more information about secured production mode, see the [secured production mode](https://docs.oracle.com/en/middleware/fusion-middleware/weblogic-server/12.2.1.4/lockd/secure.html#GUID-ADF914EF-0FB6-446E-B6BF-D230D8B0A5B0) documentation. +For more information about secured production mode, see the [secured production mode](https://docs.oracle.com/en/middleware/fusion-middleware/weblogic-server/14.1.2/secmg/using-secured-production-mode.html#GUID-9ED2EF38-F763-4999-80ED-27A3FBCB9D7D) documentation. **NOTE**: If the domain is _not_ in production mode, then none of the security changes apply. ### General upgrade procedures -In general, the process for upgrading WLS and FMW/JRF infrastructure domains in Kubernetes is similar to upgrading domains on premises. For a thorough understanding, we suggest that you read the [Fusion Middleware Upgrade Guide](https://docs.oracle.com/en/middleware/fusion-middleware/12.2.1.4/asmas/planning-upgrade-oracle-fusion-middleware-12c.html#GUID-D9CEE7E2-5062-4086-81C7-79A33A200080). +In general, the process for upgrading WLS and FMW/JRF infrastructure domains in Kubernetes is similar to upgrading domains on premises. For a thorough understanding, we suggest that you read the [Fusion Middleware Upgrade Guide](https://docs.oracle.com/en/middleware/fusion-middleware/14.1.2/asmas/planning-upgrade-oracle-fusion-middleware-12c.html). Before the upgrade, you must do the following: From ae74c3b47590a575a82d7f201a66dd913b80b4d5 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 20 Dec 2024 13:09:11 -0500 Subject: [PATCH 257/356] Updates for 14.1.2.0.0 --- .../site/content/introduction/prerequisites/introduction.md | 6 ++++-- .../accessing-the-domain/external-clients.md | 2 +- .../accessing-the-domain/remote-admin-console.md | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/documentation/site/content/introduction/prerequisites/introduction.md b/documentation/site/content/introduction/prerequisites/introduction.md index b2543ae3ac9..6b8ed53aded 100644 --- a/documentation/site/content/introduction/prerequisites/introduction.md +++ b/documentation/site/content/introduction/prerequisites/introduction.md @@ -13,7 +13,7 @@ For the current production release {{< latestVersion >}}: * Docker 19.03.1+ (check with `docker version`) *or* CRI-O 1.20.2+ (check with `crictl version | grep RuntimeVersion`). * Helm 3.3.4+ (check with `helm version --client --short`). * For domain home source type `Model in Image`, WebLogic Deploy Tooling 1.9.11+. -* Oracle WebLogic Server 12.2.1.4.0 or Oracle WebLogic Server 14.1.1.0.0. +* Oracle WebLogic Server 12.2.1.4.0, Oracle WebLogic Server 14.1.1.0.0, or Oracle WebLogic Server 14.1.2.0.0. * **NOTE**: * As of June, 2023, Oracle WebLogic Server 12.2.1.3 is no longer supported. The last Critical Patch Updates (CPU) images for WebLogic Server 12.2.1.3 were published in April, 2023. @@ -24,7 +24,9 @@ For the current production release {{< latestVersion >}}: {{% /notice %}} * Check the WLS version and patches using the [WebLogic Image Tool](https://oracle.github.io/weblogic-image-tool/userguide/tools/inspect-image/) `inspect` command: `imagetool inspect --image=container-registry.oracle.com/middleware/weblogic:12.2.1.4 --patches`. For more information, see [Inspect images]({{< relref "/base-images/ocr-images#inspect-images" >}}). * Container images based on Oracle Linux 8 are now supported. The Oracle Container Registry hosts container images - based on both Oracle Linux 7 and 8, including Oracle WebLogic Server 14.1.1.0.0 images based on Java 8 and 11. + based on both Oracle Linux 7 and 8, including Oracle WebLogic Server 14.1.1.0.0 images based on Java 8 and 11 and Oracle WebLogic Server 14.1.2.0.0 images based on Java 17 and 21. +* Container images based on Oracle Linux 9 are now supported. The Oracle Container Registry hosts container images + based on Oracle Linux 9, including Oracle WebLogic Server 14.1.2.0.0 images based on Java 17 and 21. * You must have the `cluster-admin` role to install the operator. The operator does not need the `cluster-admin` role at runtime. For more information, see the role-based access control, operator diff --git a/documentation/site/content/managing-domains/accessing-the-domain/external-clients.md b/documentation/site/content/managing-domains/accessing-the-domain/external-clients.md index ca360c30061..9b9eea5bd6d 100644 --- a/documentation/site/content/managing-domains/accessing-the-domain/external-clients.md +++ b/documentation/site/content/managing-domains/accessing-the-domain/external-clients.md @@ -493,7 +493,7 @@ with domain1, such as `t3://domain1-managed-server1:8001`, will be sent to the p RMI forwarding will ensure that the message will reach WebLogic Server managed-server1 in domain1. Patch 32408938 is required in each WebLogic Server instance that participates in cross-domain transactions, or that is the routing -destination of a proxy. The patch is available for WebLogic versions 12.2.1.4.0 (PS4), and 14.1.1.0.0, and is already included in the PSUs for these releases since July 2022. +destination of a proxy. The patch is available for WebLogic versions 12.2.1.4.0 (PS4), 14.1.1.0.0 and 14.1.2.0.0, and is already included in the PSUs for these releases since July 2022. ### Configuring WebLogic Server affinity load balancing algorithms diff --git a/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md b/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md index e89970945fe..bab9ebbee05 100644 --- a/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md +++ b/documentation/site/content/managing-domains/accessing-the-domain/remote-admin-console.md @@ -45,7 +45,7 @@ To set up access to WebLogic Server domains running in Kubernetes using the Remo **NOTE**: These instructions assume that you are installing and running the Remote Console externally to your Kubernetes cluster. -1. For [additional functionality](https://oracle.github.io/weblogic-remote-console/setup/console/#ext), incorporate and deploy the WebLogic Remote Console extension in your 12.2.1.4 and 14.1.1 domains. **NOTE**: As a best practice, make sure that you are using the same versions of the WebLogic Remote Console and the WebLogic Remote Console Extension, otherwise you might lose functionality. +1. For [additional functionality](https://oracle.github.io/weblogic-remote-console/setup/console/#ext), incorporate and deploy the WebLogic Remote Console extension in your 12.2.1.4 14.1.1, and 14.1.2 domains. **NOTE**: As a best practice, make sure that you are using the same versions of the WebLogic Remote Console and the WebLogic Remote Console Extension, otherwise you might lose functionality. a. From [https://github.com/oracle/weblogic-remote-console/releases](https://github.com/oracle/weblogic-remote-console/releases), download the Remote Console extension WAR file, [console-rest-ext-[version].war](https://github.com/oracle/weblogic-remote-console/releases/download/v2.4.10/console-rest-ext-9.0.war). From aa1bd70d9dd9c88904bdaee3a87e8d4f2d558960 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 20 Dec 2024 13:13:19 -0500 Subject: [PATCH 258/356] Prepare for WKO 4.2.13 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 87b62effde2..fc6f61af334 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 5de2af399ed..b43fb70c71f 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 3ec2cd94413..4585630e73c 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 354539fd045..fb684a374b4 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.13-SNAPSHOT + 4.2.13 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 771586fe99b..4d4ab8a4144 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 3e4805c11ff..2eb693e5c43 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.13-SNAPSHOT + 4.2.13 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index f552b59c42e..136f99d4498 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 2c90cc22266..9a73c3fbf0b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 012e68c3f17..e8484d868a5 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13-SNAPSHOT + 4.2.13 operator-swagger From d0542a30e44616a5b3ae16cd1dcde47bb87b3fb9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 20 Dec 2024 13:41:58 -0500 Subject: [PATCH 259/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index fc6f61af334..0a8607d63b7 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index b43fb70c71f..598914507f0 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 4585630e73c..ff7518ad839 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index fb684a374b4..60be74e3f4c 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.13 + 4.2.14-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 4d4ab8a4144..3d77bd3b4aa 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 2eb693e5c43..452aac0e9cf 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.13 + 4.2.14-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 136f99d4498..2420019a580 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 9a73c3fbf0b..b2b286ad126 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index e8484d868a5..08d868751f0 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.13 + 4.2.14-SNAPSHOT operator-swagger From c794a72347e5037d8b786dd3fd277c65cb7b150b Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Thu, 26 Dec 2024 14:47:00 +0000 Subject: [PATCH 260/356] changed k8s version to 1.31.1 --- Jenkinsfile.oke | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index b3398caa805..bd7fb36b961 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -83,14 +83,14 @@ pipeline { string(name: 'OKE_KUBE_VERSION', description: 'kube version for oke cluster', - defaultValue: '1.30.1' + defaultValue: '1.31.1' ) string(name: 'IMAGE_ID', description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', //defaultValue OKE1.26.2: 'ocid1.image.oc1.phx.aaaaaaaaaizmtmozeudeeuq7o5ir7dkl2bkxbbb3tgomshqbqn6jpomrsjza' //1.27.2 oke defaultValue: 'ocid1.image.oc1.phx.aaaaaaaaypr5r5drojwytghw6e6mvpjsscrnkuwtmqlmvmix7kjb2zcnc7wa' //1.29.1 'ocid1.image.oc1.phx.aaaaaaaa22u45gr3ikxnc6rius2qil2kz5k3e7p476c4usr6qnvql4l5dxea' - defaultValue: 'ocid1.image.oc1.phx.aaaaaaaahgrs3zcwrvutjtni557ttrt62uggseijsmqxacr7dym423uaokcq' + defaultValue: 'ocid1.image.oc1.phx.aaaaaaaatkz7laillswel25edlbmq67ino2y2pv6xhp2lml2vzha44ao5gtq' ) string(name: 'KUBECTL_VERSION', @@ -193,7 +193,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.3' + defaultValue: '2.2.2' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', From 5168527ab9ba3518861f100f8430ca06d7a14d6a Mon Sep 17 00:00:00 2001 From: xian_cao Date: Wed, 8 Jan 2025 14:45:51 +0000 Subject: [PATCH 261/356] fix intermittent test failures in ocne nightly run --- .../kubernetes/ItMaxConcurOptions.java | 4 +- .../kubernetes/ItMiiDynamicUpdatePart1.java | 3 +- .../kubernetes/ItMiiUpdateDomainConfig.java | 241 +++++++++--------- 3 files changed, 122 insertions(+), 126 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java index 7394b40146b..47888f9ba03 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, 2024, Oracle and/or its affiliates. +// Copyright (c) 2023, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -89,7 +89,7 @@ @DisplayName("Test to a create model in image domain with Cluster Resourcees") @IntegrationTest @Tag("kind-parallel") -@Tag("olcne-mrg") +@Tag("olcne-sequential") class ItMaxConcurOptions { private static String opNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java index bfae69cdbce..afb5ba361d4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiDynamicUpdatePart1.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2024, Oracle and/or its affiliates. +// Copyright (c) 2021, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -140,7 +140,6 @@ public void beforeEach() { @Test @Order(1) @DisplayName("Add a work manager to a model-in-image domain using dynamic update") - @Tag("crio") void testMiiAddWorkManager() { // This test uses the WebLogic domain created in BeforeAll method diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java index 2d952a59d6f..d8528f71b33 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiUpdateDomainConfig.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -116,7 +116,7 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DisplayName("Test logHome on PV, add SystemResources, Clusters to model in image domain") @IntegrationTest -@Tag("olcne-mrg") +@Tag("olcne-sequential") @Tag("kind-parallel") @Tag("toolkits-srg") @Tag("okd-wls-srg") @@ -192,7 +192,6 @@ public static void initAll(@Namespaces(2) List namespaces) { configMapName, domainUid, domainNamespace, Arrays.asList(MODEL_DIR + "/model.sysresources.yaml")); - // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); @@ -257,7 +256,7 @@ public void beforeEach() { * Check the environment variable with special characters. */ @Test - @Order(0) + @Order(1) @DisplayName("Check environment variable with special characters") void testMiiCustomEnv() { DomainResource domain1 = assertDoesNotThrow(() -> getDomainCustomResource(domainUid, domainNamespace), @@ -271,7 +270,7 @@ void testMiiCustomEnv() { if (envList.get(i).getName().equalsIgnoreCase("CUSTOM_ENV")) { assertTrue(envList.get(i).getValue() != null && envList.get(i).getValue().equalsIgnoreCase("${DOMAIN_UID}~##!'%*$(ls)"), - "Expected value for CUSTOM_ENV variable does not mtach"); + "Expected value for CUSTOM_ENV variable does not match"); found = true; } } @@ -320,7 +319,7 @@ void testMiiCustomEnv() { * The test looks for the string RUNNING in the server log */ @Test - @Order(1) + @Order(2) @DisplayName("Check the server logs are written to PersistentVolume") void testMiiServerLogsAreOnPV() { // check server logs are written on PV and look for string RUNNING in log @@ -334,7 +333,7 @@ void testMiiServerLogsAreOnPV() { * logs */ @Test - @Order(2) + @Order(3) @DisplayName("Check the HTTP server logs are written to PersistentVolume") void testMiiHttpServerLogsAreOnPV() { String[] podNames = {managedServerPrefix + "1", managedServerPrefix + "2"}; @@ -369,7 +368,7 @@ void testMiiHttpServerLogsAreOnPV() { * using the public node port of the administration server. */ @Test - @Order(3) + @Order(4) @DisplayName("Verify the pre-configured SystemResources in the domain") void testMiiCheckSystemResources() { @@ -424,6 +423,84 @@ void testMiiCheckSystemResources() { } } + /** + * Start a WebLogic domain using model-in-image with JMS/JDBC SystemResources. + * Create a empty configmap to delete JMS/JDBC SystemResources + * Patch the domain resource with the configmap. + * Update the restart version of the domain resource. + * Verify rolling restart of the domain by comparing PodCreationTimestamp + * for all the server pods before and after rolling restart. + * Verify SystemResources are deleted from the domain. + */ + @Test + @Order(5) + @DisplayName("Delete SystemResources from the domain") + void testMiiDeleteSystemResourcesByEmptyConfigMap() { + + String configMapName = "deletesysrescm"; + createConfigMapAndVerify( + configMapName, domainUid, domainNamespace, + Arrays.asList(MODEL_DIR + "/model.delete.sysresourcesbyconfigmap.yaml")); + + LinkedHashMap pods = new LinkedHashMap<>(); + // get the creation time of the admin server pod before patching + OffsetDateTime adminPodCreationTime = getPodCreationTime(domainNamespace,adminServerPodName); + pods.put(adminServerPodName, adminPodCreationTime); + // get the creation time of the managed server pods before patching + for (int i = 1; i <= replicaCount; i++) { + pods.put(managedServerPrefix + i, getPodCreationTime(domainNamespace, managedServerPrefix + i)); + } + + StringBuffer patchStr = null; + patchStr = new StringBuffer("[{"); + patchStr.append("\"op\": \"replace\",") + .append(" \"path\": \"/spec/configuration/model/configMap\",") + .append(" \"value\": \"" + configMapName + "\"") + .append(" }]"); + logger.log(Level.INFO, "Configmap patch string: {0}", patchStr); + + patch = new V1Patch(new String(patchStr)); + boolean cmPatched = assertDoesNotThrow(() -> + patchDomainCustomResource(domainUid, domainNamespace, patch, "application/json-patch+json"), + "patchDomainCustomResource(configMap) failed "); + assertTrue(cmPatched, "patchDomainCustomResource(configMap) failed"); + + String newRestartVersion = patchDomainResourceWithNewRestartVersion(domainUid, domainNamespace); + logger.log(Level.INFO, "New restart version is {0}", newRestartVersion); + + assertTrue(verifyRollingRestartOccurred(pods, 1, domainNamespace), + "Rolling restart failed"); + + // Even if pods are created, need the service to created + for (int i = 1; i <= replicaCount; i++) { + logger.info("Check managed server service {0} created in namespace {1}", + managedServerPrefix + i, domainNamespace); + checkServiceExists(managedServerPrefix + i, domainNamespace); + } + + if (OKE_CLUSTER || OCNE) { + String resourcePath = "/management/weblogic/latest/domainConfig/JDBCSystemResources/TestDataSource"; + ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); + assertEquals(0, result.exitValue(), "Failed to delete the JDBCSystemResource configuration"); + assertTrue(result.toString().contains("404"), "Failed to delete the JDBCSystemResource configuration"); + logger.info("The JDBCSystemResource configuration is deleted"); + + resourcePath = "/management/weblogic/latest/domainConfig/JMSSystemResources/TestClusterJmsModule"; + result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); + assertEquals(0, result.exitValue(), "Failed to delete the JMSSystemResources configuration"); + assertTrue(result.toString().contains("404"), "Failed to delete the JMSSystemResources configuration"); + logger.info("The JMSSystemResource configuration is deleted"); + } else { + int adminServiceNodePort + = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); + assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid"); + verifySystemResourceConfiguration(adminSvcExtHost, adminServiceNodePort, + "JDBCSystemResources", "TestDataSource", "404", hostHeader); + verifySystemResourceConfiguration(adminSvcExtHost, adminServiceNodePort, + "JMSSystemResources", "TestClusterJmsModule", "404", hostHeader); + } + } + /** * Start a WebLogic domain using model-in-image. * Create 1 configmap with 2 models files, one of them to add JMS/JDBC SystemResources @@ -435,7 +512,7 @@ void testMiiCheckSystemResources() { * Verify SystemResources are deleted from the domain. */ @Test - @Order(5) + @Order(6) @DisplayName("Delete SystemResources from the domain") void testMiiDeleteSystemResources() { @@ -516,7 +593,7 @@ void testMiiDeleteSystemResources() { * Verify JMS Server logs are written on PV. */ @Test - @Order(6) + @Order(7) @DisplayName("Add new JDBC/JMS SystemResources to the domain") void testMiiAddSystemResources() { @@ -604,7 +681,7 @@ void testMiiAddSystemResources() { * Verify servers from the new cluster are running. */ @Test - @Order(7) + @Order(8) @DisplayName("Add a dynamic cluster to domain with non-zero replica count") void testMiiAddDynamicCluster() { @@ -676,7 +753,7 @@ void testMiiAddDynamicCluster() { * Check the validity of new credentials by accessing WebLogic RESTful Service */ @Test - @Order(8) + @Order(9) @DisplayName("Change the WebLogic Admin credential of the domain") void testMiiUpdateWebLogicCredential() { verifyUpdateWebLogicCredential(7001, domainNamespace, domainUid, adminServerPodName, @@ -698,7 +775,7 @@ void testMiiUpdateWebLogicCredential() { * Make sure JMS Connections and messages are distributed across 4 servers. */ @Test - @Order(9) + @Order(10) @DisplayName("Test modification to Dynamic cluster size parameters") void testMiiUpdateDynamicClusterSize() { @@ -714,7 +791,7 @@ void testMiiUpdateDynamicClusterSize() { checkPodReadyAndServiceExists(managedServerPrefix + "4", domainUid, domainNamespace); checkPodReadyAndServiceExists(managedServerPrefix + "5", domainUid, domainNamespace); - // Make sure that we can scale down upto replica count 1 + // Make sure that we can scale down replica count to 1 // since the MinDynamicClusterSize is set to 1 logger.info("[Before Patching] updating the replica count to 1"); boolean p11Success = scaleCluster(domainUid + "-cluster-1", domainNamespace, 1); @@ -814,112 +891,6 @@ void testMiiUpdateDynamicClusterSize() { logger.info("New Dynamic Cluster Size attribute verified"); } - // Build JMS Client inside the Admin Server Pod - private void buildClientOnPod() { - - String destLocation = "/u01/JmsTestClient.java"; - assertDoesNotThrow(() -> copyFileToPod(domainNamespace, - adminServerPodName, "", - Paths.get(RESOURCE_DIR, "tunneling", "JmsTestClient.java"), - Paths.get(destLocation))); - - String jarLocation = "/u01/oracle/wlserver/server/lib/weblogic.jar"; - - StringBuffer javacCmd = new StringBuffer(KUBERNETES_CLI + " exec -n "); - javacCmd.append(domainNamespace); - javacCmd.append(" -it "); - javacCmd.append(adminServerPodName); - javacCmd.append(" -- /bin/bash -c \""); - javacCmd.append("javac -cp "); - javacCmd.append(jarLocation); - javacCmd.append(" /u01/JmsTestClient.java "); - javacCmd.append(" \""); - logger.info("javac command {0}", javacCmd.toString()); - ExecResult result = assertDoesNotThrow( - () -> exec(new String(javacCmd), true)); - logger.info("javac returned {0}", result.toString()); - logger.info("javac returned EXIT value {0}", result.exitValue()); - assertEquals(0, result.exitValue(), "Client compilation fails"); - } - - /** - * Start a WebLogic domain using model-in-image with JMS/JDBC SystemResources. - * Create a empty configmap to delete JMS/JDBC SystemResources - * Patch the domain resource with the configmap. - * Update the restart version of the domain resource. - * Verify rolling restart of the domain by comparing PodCreationTimestamp - * for all the server pods before and after rolling restart. - * Verify SystemResources are deleted from the domain. - */ - @Test - @Order(4) - @DisplayName("Delete SystemResources from the domain") - void testMiiDeleteSystemResourcesByEmptyConfigMap() { - - String configMapName = "deletesysrescm"; - createConfigMapAndVerify( - configMapName, domainUid, domainNamespace, - Arrays.asList(MODEL_DIR + "/model.delete.sysresourcesbyconfigmap.yaml")); - - LinkedHashMap pods = new LinkedHashMap<>(); - // get the creation time of the admin server pod before patching - OffsetDateTime adminPodCreationTime = getPodCreationTime(domainNamespace,adminServerPodName); - pods.put(adminServerPodName, adminPodCreationTime); - // get the creation time of the managed server pods before patching - for (int i = 1; i <= replicaCount; i++) { - pods.put(managedServerPrefix + i, getPodCreationTime(domainNamespace, managedServerPrefix + i)); - } - - StringBuffer patchStr = null; - patchStr = new StringBuffer("[{"); - patchStr.append("\"op\": \"replace\",") - .append(" \"path\": \"/spec/configuration/model/configMap\",") - .append(" \"value\": \"" + configMapName + "\"") - .append(" }]"); - logger.log(Level.INFO, "Configmap patch string: {0}", patchStr); - - patch = new V1Patch(new String(patchStr)); - boolean cmPatched = assertDoesNotThrow(() -> - patchDomainCustomResource(domainUid, domainNamespace, patch, "application/json-patch+json"), - "patchDomainCustomResource(configMap) failed "); - assertTrue(cmPatched, "patchDomainCustomResource(configMap) failed"); - - String newRestartVersion = patchDomainResourceWithNewRestartVersion(domainUid, domainNamespace); - logger.log(Level.INFO, "New restart version is {0}", newRestartVersion); - - assertTrue(verifyRollingRestartOccurred(pods, 1, domainNamespace), - "Rolling restart failed"); - - // Even if pods are created, need the service to created - for (int i = 1; i <= replicaCount; i++) { - logger.info("Check managed server service {0} created in namespace {1}", - managedServerPrefix + i, domainNamespace); - checkServiceExists(managedServerPrefix + i, domainNamespace); - } - - if (OKE_CLUSTER || OCNE) { - String resourcePath = "/management/weblogic/latest/domainConfig/JDBCSystemResources/TestDataSource"; - ExecResult result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); - assertEquals(0, result.exitValue(), "Failed to delete the JDBCSystemResource configuration"); - assertTrue(result.toString().contains("404"), "Failed to delete the JDBCSystemResource configuration"); - logger.info("The JDBCSystemResource configuration is deleted"); - - resourcePath = "/management/weblogic/latest/domainConfig/JMSSystemResources/TestClusterJmsModule"; - result = exeAppInServerPod(domainNamespace, adminServerPodName, 7001, resourcePath); - assertEquals(0, result.exitValue(), "Failed to delete the JMSSystemResources configuration"); - assertTrue(result.toString().contains("404"), "Failed to delete the JMSSystemResources configuration"); - logger.info("The JMSSystemResource configuration is deleted"); - } else { - int adminServiceNodePort - = getServiceNodePort(domainNamespace, getExternalServicePodName(adminServerPodName), "default"); - assertNotEquals(-1, adminServiceNodePort, "admin server default node port is not valid"); - verifySystemResourceConfiguration(adminSvcExtHost, adminServiceNodePort, - "JDBCSystemResources", "TestDataSource", "404", hostHeader); - verifySystemResourceConfiguration(adminSvcExtHost, adminServiceNodePort, - "JMSSystemResources", "TestClusterJmsModule", "404", hostHeader); - } - } - // Run standalone JMS Client in the pod using wlthint3client.jar in classpath. // The client sends 300 messsage to a Uniform Distributed Queue. // Make sure that each destination get excatly 150 messages each. @@ -933,7 +904,6 @@ private static Callable runJmsClient(String javaCmd) { }); } - private static void createDatabaseSecret( String secretName, String username, String password, String dburl, String domNamespace) { @@ -1112,7 +1082,6 @@ private void verifyJdbcRuntime(String resourcesName, String expectedOutput) { headers = " -H 'host: " + hostHeader + "' "; } - ExecResult result = null; curlString = new StringBuffer("curl -g --user ") .append(ADMIN_USERNAME_DEFAULT) .append(":") @@ -1150,4 +1119,32 @@ private void checkLogsOnPV(String commandToExecuteInsidePod, String podName) { String.format("Command %s failed with exit value %s, stderr %s, stdout %s", commandToExecuteInsidePod, result.exitValue(), result.stderr(), result.stdout())); } + + // Build JMS Client inside the Admin Server Pod + private void buildClientOnPod() { + + String destLocation = "/u01/JmsTestClient.java"; + assertDoesNotThrow(() -> copyFileToPod(domainNamespace, + adminServerPodName, "", + Paths.get(RESOURCE_DIR, "tunneling", "JmsTestClient.java"), + Paths.get(destLocation))); + + String jarLocation = "/u01/oracle/wlserver/server/lib/weblogic.jar"; + + StringBuffer javacCmd = new StringBuffer(KUBERNETES_CLI + " exec -n "); + javacCmd.append(domainNamespace); + javacCmd.append(" -it "); + javacCmd.append(adminServerPodName); + javacCmd.append(" -- /bin/bash -c \""); + javacCmd.append("javac -cp "); + javacCmd.append(jarLocation); + javacCmd.append(" /u01/JmsTestClient.java "); + javacCmd.append(" \""); + logger.info("javac command {0}", javacCmd.toString()); + ExecResult result = assertDoesNotThrow( + () -> exec(new String(javacCmd), true)); + logger.info("javac returned {0}", result.toString()); + logger.info("javac returned EXIT value {0}", result.exitValue()); + assertEquals(0, result.exitValue(), "Client compilation fails"); + } } From ee888031a7a7b8bc123a26c9cd291f42876b1fdc Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 10 Jan 2025 15:22:28 -0500 Subject: [PATCH 262/356] Synchronize documentation --- .../upgrade-14210.md | 991 +++++----- .../model-in-image/overview.md | 208 ++- .../model-in-image/runtime-updates.md | 1628 +++++++++-------- 3 files changed, 1434 insertions(+), 1393 deletions(-) diff --git a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md index acbb37245d3..7ab4537a31a 100644 --- a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md +++ b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md @@ -1,496 +1,495 @@ -+++ -title = "Upgrade managed domains to v14.1.2.0" -date = 2023-10-05T16:43:45-05:00 -weight = 7 -draft = true -pre = " " -description = "Upgrade managed domains to v14.1.2.0." -+++ - -{{< table_of_contents >}} - -This document provides guidelines for upgrading WLS and FMW/JRF infrastructure domains to v14.1.2.0. - -### Important considerations - -By default, version 14.1.2.0 WLS and FMW/JRF infrastructure domains _in production mode_ are set to **secured production mode**, in which their default security configuration -is more secure, insecure configurations are logged as warnings, and default authorization and -role mapping policies are more restrictive. - -Some important secured production mode changes are: - -* Plain HTTP listen ports are disabled. Any application code, utilities, or ingresses that use plain HTTP listen ports must be changed. - -* SSL listen ports must be enabled for every server in the domain. Each server must have at least one SSL listen port set up, either in the default channel or in one of the custom network channels. Note that demo SSL certificates should **not** be used in a production environment; you should set up SSL listen ports with valid SSL certificates in all server instances. - -* Demo SSL certificates have been changed completely from previous releases; there are special considerations when using them. For more information, see [Using demo SSL certificates in v14.1.2.0.0 or later]({{< relref "/managing-domains/model-in-image/overview#using-demo-ssl-certificates-in-v141200-or-later" >}}). - - -For more information about secured production mode, see the [secured production mode](https://docs.oracle.com/en/middleware/fusion-middleware/weblogic-server/14.1.2/secmg/using-secured-production-mode.html#GUID-9ED2EF38-F763-4999-80ED-27A3FBCB9D7D) documentation. - -**NOTE**: If the domain is _not_ in production mode, then none of the security changes apply. - -### General upgrade procedures - -In general, the process for upgrading WLS and FMW/JRF infrastructure domains in Kubernetes is similar to upgrading domains on premises. For a thorough understanding, we suggest that you read the [Fusion Middleware Upgrade Guide](https://docs.oracle.com/en/middleware/fusion-middleware/14.1.2/asmas/planning-upgrade-oracle-fusion-middleware-12c.html). - -Before the upgrade, you must do the following: - -- If your [domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}) is Domain on Persistent Volume (DoPV), then back up the domain home. -- If your domain type is `JRF`, then: - - Back up the JRF database. - - Back up the OPSS wallet file. See [Save the OPSS wallet secret](#back-up-the-opss-wallet-and-save-it-in-a-secret). - - Make sure nothing else is accessing the database. -- **Do not delete** the domain resource. -- Shut down the domain by patching the domain and/or cluster spec `serverStartPolicy` to `Never`. For example: - ``` - $ kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}]' - ``` - - -If your domain is on a persistent volume, WebLogic provides two utilities for performing version upgrades of WebLogic domains: the Upgrade Assistant for upgrading FMW JRF database schemas and the Reconfiguration Wizard for upgrading the domain configuration. When running these utilities, you will need access to the existing domain home directory. -Because a typical Kubernetes environment lacks a graphical interface, you must run these utilities with the command-line options. - -If your domain is using Model in Image, because the domain will be rebuilt when the model is updated, see the [Upgrade Use Cases](#upgrade-use-cases) for details. - -#### Back up the OPSS wallet and save it in a secret - -The operator provides a helper script, the [OPSS wallet utility](https://orahub.oci.oraclecorp.com/weblogic-cloud/weblogic-kubernetes-operator/-/blob/main/kubernetes/samples/scripts/domain-lifecycle/opss-wallet.sh), for extracting the wallet file and storing it in a Kubernetes `walletFileSecret`. In addition, you should save the wallet file in a safely backed-up location, outside of Kubernetes. For example, the following command saves the OPSS wallet for the `sample-domain1` domain in the `sample-ns` namespace to a file named `ewallet.p12` in the `/tmp` directory and also stores it in the wallet secret named `sample-domain1-opss-walletfile-secret`. - -``` -$ opss-wallet.sh -n sample-ns -d sample-domain1 -s -r -wf /tmp/ewallet.p12 -ws sample-domain1-opss-walletfile-secret -``` - -#### Deploy a WebLogic Server pod to access the domain home on a persistent volume - -For domain on persistent volume, you will need to access the domain home on the shared volume with the 14.1.2.0 WebLogic Server version pod. -You can launch a running pod with the [PV and PVC helper script](https://github.com/oracle/weblogic-kubernetes-operator/blob/main/kubernetes/samples/scripts/domain-lifecycle/pv-pvc-helper.sh). - -For example, - -```shell -$ ./pv-pvc-helper.sh -n sample-domain1-ns -c sample-domain1-pvc-rwm1 -m /share -i wls14120:fmw -``` - -After the pod is deployed, you can `kubectl -n sample-domain1-ns exec -it pvhelper -- /bin/sh'` into the pod's terminal session. - -#### Upgrade the JRF database - -The Upgrade Assistant is for upgrading schemas in a JRF database. It will detect if any schema needs to be upgraded, then upgrade the schemas and also upgrade the system-owned schema version table. - -If you have not yet deployed a WebLogic Server pod, see [Deploy the server pod](#deploy-a-weblogic-server-pod-attaching-a-persistent-volume). - -From the `pvhelper` pod, to discover all the command-line options: - -```shell -$ cd $ORACLE_HOME/oracle_common/upgrade/bin -$ ./ua -help -``` - -Create a file named `response.txt` with this content and modify any ` - -# The next section contains the information for performing a schema -# upgrade on Oracle Platform Security Services, as described in the Upgrade -# Descriptor file located at -# /u01/oracle/oracle_common/plugins/upgrade/Opss.xml - -# Do not change the next line. -[OPSS.OPSS_SCHEMA_PLUGIN] - -# The following number uniquely identifies this instance of an -# upgrade plugin. Do not change it. -pluginInstance = 10 - -# The next few lines describe a database connection. -# "Specify the database containing the OPSS schema." -# Specifies the type of database. Supported types for this product are -# Oracle Database, Oracle Database enabled for edition-based redefinition, Microsoft SQL Server, IBM DB2 - -OPSS.databaseType = Oracle Database - -# Specifies the database connection string for the DBA user. -# The format depends upon the database type. - -#OPSS.databaseConnectionString = //nuc:1521/orclpdb1 -OPSS.databaseConnectionString = - -# Specifies the database connection string for the user schema. -# The format depends upon the database type. - -#OPSS.schemaConnectionString = //nuc:1521/orclpdb1 -OPSS.schemaConnectionString = - -# Specifies the name of the schema or database user - -#OPSS.schemaUserName = FMWTEST_OPSS -OPSS.schemaUserName = - -# Specifies the password for the schema, in encrypted form. -# To specify a different password in cleartext, use the "cleartextSchemaPassword" keyword instead: - -OPSS.cleartextSchemaPassword = - -# encrypted password can be generated with command line option -createResponse -#OPSS.encryptedSchemaPassword = 0551CF2EACFC4FE7BCB1F860FCF68E13AA6E61A724E7CFC09E -# Specifies the name of the database administrator account. - -OPSS.dbaUserName = - -# Specifies the password for the database administrator account, in encrypted form. -# To specify a different password in cleartext, use the "cleartextDbaPassword" keyword -# instead: - -OPSS.cleartextDbaPassword = - -#OPSS.encryptedDbaPassword = 057B3698F71FB2EE583D32EF36234174DCC2C7276FC11F77E7 - -# The next section contains the information for performing a schema -# upgrade on Oracle Metadata Services, as described in the Upgrade -# Descriptor file located at -# /u01/oracle/oracle_common/plugins/upgrade/mds.xml -# Do not change the next line. - -[MDS.SCHEMA_UPGRADE] -pluginInstance = 11 - -MDS.databaseConnectionString = -MDS.schemaConnectionString = -MDS.schemaUserName = -MDS.cleartextSchemaPassword = -MDS.dbaUserName = -MDS.cleartextDbaPassword = - -# The next section contains the information for performing a schema -# upgrade on Oracle Audit Services, as described in the Upgrade -# Descriptor file located at -# /u01/oracle/oracle_common/plugins/upgrade/audit.xml -# Do not change the next line. - -[IAU.AUDIT_SCHEMA_PLUGIN] -pluginInstance = 6 - -IAU.databaseType = Oracle Database -IAU.databaseConnectionString = -IAU.schemaConnectionString = -IAU.schemaUserName = -IAU.cleartextSchemaPassword = -IAU.dbaUserName = -IAU.cleartextDbaPassword = - - -# The next section contains the information for performing a schema -# upgrade on Common Infrastructure Services, as described in the Upgrade -# Descriptor file located at -# /u01/oracle/oracle_common/plugins/upgrade/cie.xml -# Do not change the next line. - -[FMWCONFIG.CIE_SCHEMA_PLUGIN] -pluginInstance = 4 - -STB.databaseType = Oracle Database -STB.databaseConnectionString = -STB.schemaConnectionString = -STB.schemaUserName = -STB.cleartextSchemaPassword = -STB.dbaUserName = -STB.cleartextDbaPassword = - -# This secion is not needed for pure JRF domain. - -# The next section contains the information for performing a schema -# upgrade on Oracle WebLogicServer, as described in the Upgrade -# Descriptor file located at -# /u01/oracle/oracle_common/plugins/upgrade/wlsservices.xml -# Do not change the next line. - -#[WLS.WLS] -#pluginInstance = 7 - -#WLS.databaseType = Oracle Database -#WLS.databaseConnectionString = -#WLS.schemaConnectionString = -#WLS.schemaUserName = -#WLS.encryptedSchemaPassword = 05FEC474FC653B49B15ED79A53565A8B00F49ADADA72D30816 -#WLS.dbaUserName = -# WLS.cleartextDbaPassword = -#WLS.encryptedDbaPassword = 0543C93F9A28FBAFBF3FCC49E78EB2C6B3AA02F53098BB322C - -``` - -Copy the response file to the pod. - -```shell -$ kubectl -n sample-domain1-ns cp response.txt pvhelper:/tmp -``` - -Run the Upgrade Assistant readiness check to verify the input values and whether the schema needs to be upgraded. - -```shell -$ ./ua -readiness -response /tmp/response.txt -logDir /tmp -``` - -Check the output to see if there are any errors. - -``` -Oracle Fusion Middleware Upgrade Assistant 14.1.2.0.0 -Log file is located at: /tmp/ua2023-10-04-17-23-32PM.log -Reading installer inventory, this will take a few moments... -...completed reading installer inventory. -Using response file /tmp/response.txt for input - Oracle Metadata Services schema readiness check is in progress - Oracle Audit Services schema readiness check is in progress - Oracle Platform Security Services schema readiness check is in progress - Common Infrastructure Services schema readiness check is in progress - Common Infrastructure Services schema readiness check finished with status: ready for upgrade - Oracle Metadata Services schema readiness check finished with status: ready for upgrade - Oracle Audit Services schema readiness check finished with status: ready for upgrade - Oracle Platform Security Services schema readiness check finished with status: ready for upgrade -Readiness Check Report File: /tmp/readiness2023-10-04-17-24-55PM.txt -Upgrade readiness check completed successfully. -UPGAST-00281: Upgrade is being skipped because the -readiness flag is set -Actual upgrades are not done when the -readiness command line option is set. -If you want to perform an actual upgrade remove the -readiness flag from the command line. If you intended to perform just the readiness phase, no action is necessary. -``` - -If there are no errors and you are ready to upgrade, then run the command again without the `-readiness` flag. - -```shell -$ ./ua -response /tmp/response.txt -logDir /tmp -``` - -Check the output again. - -``` -Oracle Fusion Middleware Upgrade Assistant 14.1.2.0.0 -Log file is located at: /u01/oracle/oracle_common/upgrade/logs/ua2023-10-05-14-03-18PM.log -Reading installer inventory, this will take a few moments... -...completed reading installer inventory. -Using response file /tmp/response.txt for input - Oracle Platform Security Services schema examine is in progress - Oracle Metadata Services schema examine is in progress - Oracle Audit Services schema examine is in progress - Common Infrastructure Services schema examine is in progress - Common Infrastructure Services schema examine finished with status: ready for upgrade - Oracle Platform Security Services schema examine finished with status: ready for upgrade - Oracle Audit Services schema examine finished with status: ready for upgrade - Oracle Metadata Services schema examine finished with status: ready for upgrade -Schema Version Registry saved to: /u01/oracle/oracle_common/upgrade/logs/ua2023-10-05-14-03-18PM.xml - Oracle Platform Security Services schema upgrade is in progress - Oracle Audit Services schema upgrade is in progress - Oracle Metadata Services schema upgrade is in progress - Common Infrastructure Services schema upgrade is in progress - Common Infrastructure Services schema upgrade finished with status: succeeded - Oracle Audit Services schema upgrade finished with status: succeeded - Oracle Platform Security Services schema upgrade finished with status: succeeded - Oracle Metadata Services schema upgrade finished with status: succeeded -``` - -If there are any errors, you need to correct them or contact Oracle Support for assistance. - - -#### Reconfigure the domain - -The Reconfiguration Wizard will upgrade the domain configuration to the 14.1.2.0 version. - -If you have not yet deployed a WebLogic Server pod, see [Deploy the server pod](#deploy-a-weblogic-server-pod-attaching-a-persistent-volume). - -From the `pvhelper` pod, use the following WLST commands to reconfigure a domain to the 14.1.2.0 version. - - -```shell -$ /u01/oracle/oracle_common/bin/wlst.sh - -Initializing WebLogic Scripting Tool (WLST) ... - -Welcome to WebLogic Server Administration Scripting Shell - -Type help() for help on available commands - -wls:/offline> readDomainForUpgrade('') -wls:/offline> updateDomain() -wls:/offline> closeDomain() -``` - -If there are any errors, you need to correct them or contact Oracle Support for assistance. - -### Upgrade use cases - -Consider the following use case scenarios, depending on your WebLogic domain type (`WLS` or `JRF`) and domain home source type (Domain on PV or Model in Image). - -- [WLS Domain on Persistent Volume](#wls-domain-on-persistent-volume) -- [FMW/JRF Domain on Persistent Volume](#fmwjrf-domain-on-persistent-volume) -- [WLS domain using Model in Image](#wls-domain-using-model-in-image) -- [FMW/JRF domain using Model in Image](#fmwjrf-domain-using-model-in-image) - -#### WLS Domain on Persistent Volume - -1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). You can skip the database related steps. -2. Upgrade the domain configuration using the reconfiguration WLST commands. See [Reconfigure the domain](#reconfigure-the-domain). -3. Update the domain resource to use the WebLogic 14120 base image, and patch the `serverStartPolicy` to `IfNeeded` to restart the domain. For example, - `kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}, {"op": "replace", "path":"/spec/image", "value":""]'` - -#### FMW/JRF Domain on Persistent Volume - -1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). -2. Run the Upgrade Assistant. See [Upgrade the JRF database](#upgrade-the-jrf-database). -3. Upgrade the domain configuration using the reconfiguration WLST commands. See [Reconfigure the domain](#reconfigure-the-domain). -4. Update the domain resource to use the Fusion Middleware Infrastructure 14120 base image, and patch the `serverStartPolicy` to `IfNeeded` to restart the domain. For example, - `kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}, {"op": "replace", "path":"/spec/image", "value":""]'` - - -#### WLS domain using Model in Image - -1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). -2. Depending on whether your existing domain is using secured production mode, use one of the following options. - - -| Existing Domain | Upgrade Actions | -|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Domain is already using secured production mode | Update the domain resource to use the WebLogic 14120 base image and redeploy the domain. | -| Domain is not using secured production mode but ready to switch to use secured production mode | Enable secured production mode. See the [Sample WDT YAML](#sample-wdt-model-for-secured-production-mode-and-ssl), update the domain resource to use the WebLogic 14120 base image and redeploy the domain. | -| Domain is not using secured production mode but _not_ ready to switch to use secured production mode | Update the domain resource to use the WebLogic 14120 base image and redeploy the domain, the operator will automatically disable the secured production mode for you, but your domain will not have the added benefits of a secured production mode. | -| Domain is not using secured production mode and wanted to start from scratch to rebuild the domain | Delete the domain first, enable secure production mode. See the [Sample WDT YAML](#sample-wdt-model-for-secured-production-mode-and-ssl), update the domain resource YAML to use the WebLogic 14120 base image and deploy the domain. | - -3. You can use this patch command for redeploying the domain with the new WebLogic 14120 image. For example, - `kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}, {"op": "replace", "path":"/spec/image", "value":""]'` - -#### FMW/JRF domain using Model in Image - -FMW/JRF domains using Model in Image has been deprecated since WebLogic Kubernetes Operator 4.1. Before upgrading to FMW v14.1.2.0, we recommend moving your domain home to Domain on Persistent Volume. For more information, see [Domain On Persistent Volume]({{< relref "/managing-domains/domain-on-pv/overview.md" >}}). - -1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). -2. If are not using an auxiliary image in your domain, then create a [Domain creation image]({{< relref "/managing-domains/domain-on-pv/domain-creation-images.md" >}}). -3. Create a new domain resource YAML file. You should have at least the following changes: - -``` -# Change type to PersistentVolume -domainHomeSourceType: PersistentVolume -image: -... -serverPod: - ... - # specify the volume and volume mount information - - volumes: - - name: weblogic-domain-storage-volume - persistentVolumeClaim: - claimName: sample-domain1-pvc-rwm1 - volumeMounts: - - mountPath: /share - name: weblogic-domain-storage-volume - - # specify a new configuration section, remove the old configuration section. - - configuration: - - # secrets that are referenced by model yaml macros - # sample-domain1-rcu-access is used for JRF domains - secrets: [ sample-domain1-rcu-access ] - - initializeDomainOnPV: - persistentVolumeClaim: - metadata: - name: sample-domain1-pvc-rwm1 - spec: - storageClassName: my-storage-class - resources: - requests: - storage: 10Gi - domain: - createIfNotExists: Domain - domainCreationImages: - - image: 'myaux:v6' - domainType: JRF - domainCreationConfigMap: sample-domain1-wdt-config-map - opss: - # Make sure you have already saved the wallet file secret. This allows the domain to use - # an existing JRF database schemas. - walletFileSecret: sample-domain1-opss-walletfile-secret - walletPasswordSecret: sample-domain1-opss-wallet-password-secret -``` - -4. Deploy the domain. If it is successful, then the domain has been migrated to a persistent volume. Now, you can proceed to upgrade to version 14.1.2.0, see [FMW/JRF domain on PV](#fmwjrf-domain-on-persistent-volume). - -### Sample WDT model for secured production mode and SSL - -If you are upgrading an existing domain to 14.1.2.0 and your existing domain does not have secured production mode enabled, the operator, by default, will _disable_ secured production mode. If you want to override this behavior, you must enable it explicitly. Optionally, you can delete the existing domain and let the operator completely rebuild the domain and, by default, secured production mode will be enabled; you do not have to enable it explicitly. - -The following is a code snippet of a WDT model for setting up secured production mode and SSL. - -``` -topology: - # Production mode must be true for secured production mode - # - ProductionModeEnabled: true - SecurityConfiguration: - # If you are updating an existing, pre 14.1.2.0 domain that does not have secure mode enabled, - # and want to use secured production mode, make sure you enable secure mode; otherwise the - # operator will disable it by default. - # - # - SecureMode: - SecureModeEnabled: true - # - # Make sure SSL is set up in all servers and server templates. - # - Server: - "admin-server": - CustomTrustKeyStoreFileName: 'wlsdeploy/servers/admin-server/trust-keystore.jks' - CustomIdentityKeyStoreFileName: 'wlsdeploy/servers/admin-server/identity-keystore.jks' - KeyStores: CustomIdentityAndCustomTrust - CustomIdentityKeyStoreType: JKS - CustomTrustKeyStoreType: JKS - CustomIdentityKeyStorePassPhraseEncrypted: - CustomTrustKeyStorePassPhraseEncrypted: - SSL: - ListenPort: 7002 - Enabled : true - ServerPrivateKeyAlias: adminkey - ServerPrivateKeyPassPhraseEncrypted: - ServerTemplate: - "cluster-1-template": - CustomTrustKeyStoreFileName: 'wlsdeploy/servers/managed-server/trust-keystore.jks' - CustomIdentityKeyStoreFileName: 'wlsdeploy/servers/managed-server/identity-keystore.jks' - KeyStores: CustomIdentityAndCustomTrust - CustomIdentityKeyStoreType: JKS - CustomTrustKeyStoreType: JKS - CustomIdentityKeyStorePassPhraseEncrypted: - CustomTrustKeyStorePassPhraseEncrypted: - SSL: - ListenPort: 7102 - Enabled : true - ServerPrivateKeyAlias: mykey - ServerPrivateKeyPassPhraseEncrypted: - -``` ++++ +title = "Upgrade managed domains to v14.1.2.0" +date = 2023-10-05T16:43:45-05:00 +weight = 7 +pre = " " +description = "Upgrade managed domains to v14.1.2.0." ++++ + +{{< table_of_contents >}} + +This document provides guidelines for upgrading WLS and FMW/JRF infrastructure domains to v14.1.2.0. + +### Important considerations + +By default, version 14.1.2.0 WLS and FMW/JRF infrastructure domains _in production mode_ are set to **secured production mode**, in which their default security configuration +is more secure, insecure configurations are logged as warnings, and default authorization and +role mapping policies are more restrictive. + +Some important secured production mode changes are: + +* Plain HTTP listen ports are disabled. Any application code, utilities, or ingresses that use plain HTTP listen ports must be changed. + +* SSL listen ports must be enabled for every server in the domain. Each server must have at least one SSL listen port set up, either in the default channel or in one of the custom network channels. Note that demo SSL certificates should **not** be used in a production environment; you should set up SSL listen ports with valid SSL certificates in all server instances. + +* Demo SSL certificates have been changed completely from previous releases; there are special considerations when using them. For more information, see [Using demo SSL certificates in v14.1.2.0.0 or later]({{< relref "/managing-domains/model-in-image/overview#using-demo-ssl-certificates-in-v141200-or-later" >}}). + + +For more information about secured production mode, see the [secured production mode](https://docs.oracle.com/en/middleware/fusion-middleware/weblogic-server/12.2.1.4/lockd/secure.html#GUID-ADF914EF-0FB6-446E-B6BF-D230D8B0A5B0) documentation. + +**NOTE**: If the domain is _not_ in production mode, then none of the security changes apply. + +### General upgrade procedures + +In general, the process for upgrading WLS and FMW/JRF infrastructure domains in Kubernetes is similar to upgrading domains on premises. For a thorough understanding, we suggest that you read the [Fusion Middleware Upgrade Guide](https://docs.oracle.com/en/middleware/fusion-middleware/12.2.1.4/asmas/planning-upgrade-oracle-fusion-middleware-12c.html#GUID-D9CEE7E2-5062-4086-81C7-79A33A200080). + +Before the upgrade, you must do the following: + +- If your [domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}) is Domain on Persistent Volume (DoPV), then back up the domain home. +- If your domain type is `JRF`, then: + - Back up the JRF database. + - Back up the OPSS wallet file. See [Save the OPSS wallet secret](#back-up-the-opss-wallet-and-save-it-in-a-secret). + - Make sure nothing else is accessing the database. +- **Do not delete** the domain resource. +- Shut down the domain by patching the domain and/or cluster spec `serverStartPolicy` to `Never`. For example: + ``` + $ kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}]' + ``` + + +If your domain is on a persistent volume, WebLogic provides two utilities for performing version upgrades of WebLogic domains: the Upgrade Assistant for upgrading FMW JRF database schemas and the Reconfiguration Wizard for upgrading the domain configuration. When running these utilities, you will need access to the existing domain home directory. +Because a typical Kubernetes environment lacks a graphical interface, you must run these utilities with the command-line options. + +If your domain is using Model in Image, because the domain will be rebuilt when the model is updated, see the [Upgrade Use Cases](#upgrade-use-cases) for details. + +#### Back up the OPSS wallet and save it in a secret + +The operator provides a helper script, the [OPSS wallet utility](https://orahub.oci.oraclecorp.com/weblogic-cloud/weblogic-kubernetes-operator/-/blob/main/kubernetes/samples/scripts/domain-lifecycle/opss-wallet.sh), for extracting the wallet file and storing it in a Kubernetes `walletFileSecret`. In addition, you should save the wallet file in a safely backed-up location, outside of Kubernetes. For example, the following command saves the OPSS wallet for the `sample-domain1` domain in the `sample-ns` namespace to a file named `ewallet.p12` in the `/tmp` directory and also stores it in the wallet secret named `sample-domain1-opss-walletfile-secret`. + +``` +$ opss-wallet.sh -n sample-ns -d sample-domain1 -s -r -wf /tmp/ewallet.p12 -ws sample-domain1-opss-walletfile-secret +``` + +#### Deploy a WebLogic Server pod to access the domain home on a persistent volume + +For domain on persistent volume, you will need to access the domain home on the shared volume with the 14.1.2.0 WebLogic Server version pod. +You can launch a running pod with the [PV and PVC helper script](https://github.com/oracle/weblogic-kubernetes-operator/blob/main/kubernetes/samples/scripts/domain-lifecycle/pv-pvc-helper.sh). + +For example, + +```shell +$ ./pv-pvc-helper.sh -n sample-domain1-ns -c sample-domain1-pvc-rwm1 -m /share -i wls14120:fmw +``` + +After the pod is deployed, you can `kubectl -n sample-domain1-ns exec -it pvhelper -- /bin/sh'` into the pod's terminal session. + +#### Upgrade the JRF database + +The Upgrade Assistant is for upgrading schemas in a JRF database. It will detect if any schema needs to be upgraded, then upgrade the schemas and also upgrade the system-owned schema version table. + +If you have not yet deployed a WebLogic Server pod, see [Deploy the server pod](#deploy-a-weblogic-server-pod-attaching-a-persistent-volume). + +From the `pvhelper` pod, to discover all the command-line options: + +```shell +$ cd $ORACLE_HOME/oracle_common/upgrade/bin +$ ./ua -help +``` + +Create a file named `response.txt` with this content and modify any ` + +# The next section contains the information for performing a schema +# upgrade on Oracle Platform Security Services, as described in the Upgrade +# Descriptor file located at +# /u01/oracle/oracle_common/plugins/upgrade/Opss.xml + +# Do not change the next line. +[OPSS.OPSS_SCHEMA_PLUGIN] + +# The following number uniquely identifies this instance of an +# upgrade plugin. Do not change it. +pluginInstance = 10 + +# The next few lines describe a database connection. +# "Specify the database containing the OPSS schema." +# Specifies the type of database. Supported types for this product are +# Oracle Database, Oracle Database enabled for edition-based redefinition, Microsoft SQL Server, IBM DB2 + +OPSS.databaseType = Oracle Database + +# Specifies the database connection string for the DBA user. +# The format depends upon the database type. + +#OPSS.databaseConnectionString = //nuc:1521/orclpdb1 +OPSS.databaseConnectionString = + +# Specifies the database connection string for the user schema. +# The format depends upon the database type. + +#OPSS.schemaConnectionString = //nuc:1521/orclpdb1 +OPSS.schemaConnectionString = + +# Specifies the name of the schema or database user + +#OPSS.schemaUserName = FMWTEST_OPSS +OPSS.schemaUserName = + +# Specifies the password for the schema, in encrypted form. +# To specify a different password in cleartext, use the "cleartextSchemaPassword" keyword instead: + +OPSS.cleartextSchemaPassword = + +# encrypted password can be generated with command line option -createResponse +#OPSS.encryptedSchemaPassword = 0551CF2EACFC4FE7BCB1F860FCF68E13AA6E61A724E7CFC09E +# Specifies the name of the database administrator account. + +OPSS.dbaUserName = + +# Specifies the password for the database administrator account, in encrypted form. +# To specify a different password in cleartext, use the "cleartextDbaPassword" keyword +# instead: + +OPSS.cleartextDbaPassword = + +#OPSS.encryptedDbaPassword = 057B3698F71FB2EE583D32EF36234174DCC2C7276FC11F77E7 + +# The next section contains the information for performing a schema +# upgrade on Oracle Metadata Services, as described in the Upgrade +# Descriptor file located at +# /u01/oracle/oracle_common/plugins/upgrade/mds.xml +# Do not change the next line. + +[MDS.SCHEMA_UPGRADE] +pluginInstance = 11 + +MDS.databaseConnectionString = +MDS.schemaConnectionString = +MDS.schemaUserName = +MDS.cleartextSchemaPassword = +MDS.dbaUserName = +MDS.cleartextDbaPassword = + +# The next section contains the information for performing a schema +# upgrade on Oracle Audit Services, as described in the Upgrade +# Descriptor file located at +# /u01/oracle/oracle_common/plugins/upgrade/audit.xml +# Do not change the next line. + +[IAU.AUDIT_SCHEMA_PLUGIN] +pluginInstance = 6 + +IAU.databaseType = Oracle Database +IAU.databaseConnectionString = +IAU.schemaConnectionString = +IAU.schemaUserName = +IAU.cleartextSchemaPassword = +IAU.dbaUserName = +IAU.cleartextDbaPassword = + + +# The next section contains the information for performing a schema +# upgrade on Common Infrastructure Services, as described in the Upgrade +# Descriptor file located at +# /u01/oracle/oracle_common/plugins/upgrade/cie.xml +# Do not change the next line. + +[FMWCONFIG.CIE_SCHEMA_PLUGIN] +pluginInstance = 4 + +STB.databaseType = Oracle Database +STB.databaseConnectionString = +STB.schemaConnectionString = +STB.schemaUserName = +STB.cleartextSchemaPassword = +STB.dbaUserName = +STB.cleartextDbaPassword = + +# This secion is not needed for pure JRF domain. + +# The next section contains the information for performing a schema +# upgrade on Oracle WebLogicServer, as described in the Upgrade +# Descriptor file located at +# /u01/oracle/oracle_common/plugins/upgrade/wlsservices.xml +# Do not change the next line. + +#[WLS.WLS] +#pluginInstance = 7 + +#WLS.databaseType = Oracle Database +#WLS.databaseConnectionString = +#WLS.schemaConnectionString = +#WLS.schemaUserName = +#WLS.encryptedSchemaPassword = 05FEC474FC653B49B15ED79A53565A8B00F49ADADA72D30816 +#WLS.dbaUserName = +# WLS.cleartextDbaPassword = +#WLS.encryptedDbaPassword = 0543C93F9A28FBAFBF3FCC49E78EB2C6B3AA02F53098BB322C + +``` + +Copy the response file to the pod. + +```shell +$ kubectl -n sample-domain1-ns cp response.txt pvhelper:/tmp +``` + +Run the Upgrade Assistant readiness check to verify the input values and whether the schema needs to be upgraded. + +```shell +$ ./ua -readiness -response /tmp/response.txt -logDir /tmp +``` + +Check the output to see if there are any errors. + +``` +Oracle Fusion Middleware Upgrade Assistant 14.1.2.0.0 +Log file is located at: /tmp/ua2023-10-04-17-23-32PM.log +Reading installer inventory, this will take a few moments... +...completed reading installer inventory. +Using response file /tmp/response.txt for input + Oracle Metadata Services schema readiness check is in progress + Oracle Audit Services schema readiness check is in progress + Oracle Platform Security Services schema readiness check is in progress + Common Infrastructure Services schema readiness check is in progress + Common Infrastructure Services schema readiness check finished with status: ready for upgrade + Oracle Metadata Services schema readiness check finished with status: ready for upgrade + Oracle Audit Services schema readiness check finished with status: ready for upgrade + Oracle Platform Security Services schema readiness check finished with status: ready for upgrade +Readiness Check Report File: /tmp/readiness2023-10-04-17-24-55PM.txt +Upgrade readiness check completed successfully. +UPGAST-00281: Upgrade is being skipped because the -readiness flag is set +Actual upgrades are not done when the -readiness command line option is set. +If you want to perform an actual upgrade remove the -readiness flag from the command line. If you intended to perform just the readiness phase, no action is necessary. +``` + +If there are no errors and you are ready to upgrade, then run the command again without the `-readiness` flag. + +```shell +$ ./ua -response /tmp/response.txt -logDir /tmp +``` + +Check the output again. + +``` +Oracle Fusion Middleware Upgrade Assistant 14.1.2.0.0 +Log file is located at: /u01/oracle/oracle_common/upgrade/logs/ua2023-10-05-14-03-18PM.log +Reading installer inventory, this will take a few moments... +...completed reading installer inventory. +Using response file /tmp/response.txt for input + Oracle Platform Security Services schema examine is in progress + Oracle Metadata Services schema examine is in progress + Oracle Audit Services schema examine is in progress + Common Infrastructure Services schema examine is in progress + Common Infrastructure Services schema examine finished with status: ready for upgrade + Oracle Platform Security Services schema examine finished with status: ready for upgrade + Oracle Audit Services schema examine finished with status: ready for upgrade + Oracle Metadata Services schema examine finished with status: ready for upgrade +Schema Version Registry saved to: /u01/oracle/oracle_common/upgrade/logs/ua2023-10-05-14-03-18PM.xml + Oracle Platform Security Services schema upgrade is in progress + Oracle Audit Services schema upgrade is in progress + Oracle Metadata Services schema upgrade is in progress + Common Infrastructure Services schema upgrade is in progress + Common Infrastructure Services schema upgrade finished with status: succeeded + Oracle Audit Services schema upgrade finished with status: succeeded + Oracle Platform Security Services schema upgrade finished with status: succeeded + Oracle Metadata Services schema upgrade finished with status: succeeded +``` + +If there are any errors, you need to correct them or contact Oracle Support for assistance. + + +#### Reconfigure the domain + +The Reconfiguration Wizard will upgrade the domain configuration to the 14.1.2.0 version. + +If you have not yet deployed a WebLogic Server pod, see [Deploy the server pod](#deploy-a-weblogic-server-pod-attaching-a-persistent-volume). + +From the `pvhelper` pod, use the following WLST commands to reconfigure a domain to the 14.1.2.0 version. + + +```shell +$ /u01/oracle/oracle_common/bin/wlst.sh + +Initializing WebLogic Scripting Tool (WLST) ... + +Welcome to WebLogic Server Administration Scripting Shell + +Type help() for help on available commands + +wls:/offline> readDomainForUpgrade('') +wls:/offline> updateDomain() +wls:/offline> closeDomain() +``` + +If there are any errors, you need to correct them or contact Oracle Support for assistance. + +### Upgrade use cases + +Consider the following use case scenarios, depending on your WebLogic domain type (`WLS` or `JRF`) and domain home source type (Domain on PV or Model in Image). + +- [WLS Domain on Persistent Volume](#wls-domain-on-persistent-volume) +- [FMW/JRF Domain on Persistent Volume](#fmwjrf-domain-on-persistent-volume) +- [WLS domain using Model in Image](#wls-domain-using-model-in-image) +- [FMW/JRF domain using Model in Image](#fmwjrf-domain-using-model-in-image) + +#### WLS Domain on Persistent Volume + +1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). You can skip the database related steps. +2. Upgrade the domain configuration using the reconfiguration WLST commands. See [Reconfigure the domain](#reconfigure-the-domain). +3. Update the domain resource to use the WebLogic 14120 base image, and patch the `serverStartPolicy` to `IfNeeded` to restart the domain. For example, + `kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}, {"op": "replace", "path":"/spec/image", "value":""]'` + +#### FMW/JRF Domain on Persistent Volume + +1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). +2. Run the Upgrade Assistant. See [Upgrade the JRF database](#upgrade-the-jrf-database). +3. Upgrade the domain configuration using the reconfiguration WLST commands. See [Reconfigure the domain](#reconfigure-the-domain). +4. Update the domain resource to use the Fusion Middleware Infrastructure 14120 base image, and patch the `serverStartPolicy` to `IfNeeded` to restart the domain. For example, + `kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}, {"op": "replace", "path":"/spec/image", "value":""]'` + + +#### WLS domain using Model in Image + +1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). +2. Depending on whether your existing domain is using secured production mode, use one of the following options. + + +| Existing Domain | Upgrade Actions | +|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Domain is already using secured production mode | Update the domain resource to use the WebLogic 14120 base image and redeploy the domain. | +| Domain is not using secured production mode but ready to switch to use secured production mode | Enable secured production mode. See the [Sample WDT YAML](#sample-wdt-model-for-secured-production-mode-and-ssl), update the domain resource to use the WebLogic 14120 base image and redeploy the domain. | +| Domain is not using secured production mode but _not_ ready to switch to use secured production mode | Update the domain resource to use the WebLogic 14120 base image and redeploy the domain, the operator will automatically disable the secured production mode for you, but your domain will not have the added benefits of a secured production mode. | +| Domain is not using secured production mode and wanted to start from scratch to rebuild the domain | Delete the domain first, enable secure production mode. See the [Sample WDT YAML](#sample-wdt-model-for-secured-production-mode-and-ssl), update the domain resource YAML to use the WebLogic 14120 base image and deploy the domain. | + +3. You can use this patch command for redeploying the domain with the new WebLogic 14120 image. For example, + `kubectl -n sample-domain1-ns patch domain sample-domain1 --type=json -p='[ {"op": "replace", "path": "/spec/serverStartPolicy", "value": "Never"}, {"op": "replace", "path":"/spec/image", "value":""]'` + +#### FMW/JRF domain using Model in Image + +FMW/JRF domains using Model in Image has been deprecated since WebLogic Kubernetes Operator 4.1. Before upgrading to FMW v14.1.2.0, we recommend moving your domain home to Domain on Persistent Volume. For more information, see [Domain On Persistent Volume]({{< relref "/managing-domains/domain-on-pv/overview.md" >}}). + +1. Follow the steps in the [General upgrade procedures](#general-upgrade-procedures). +2. If are not using an auxiliary image in your domain, then create a [Domain creation image]({{< relref "/managing-domains/domain-on-pv/domain-creation-images.md" >}}). +3. Create a new domain resource YAML file. You should have at least the following changes: + +``` +# Change type to PersistentVolume +domainHomeSourceType: PersistentVolume +image: +... +serverPod: + ... + # specify the volume and volume mount information + + volumes: + - name: weblogic-domain-storage-volume + persistentVolumeClaim: + claimName: sample-domain1-pvc-rwm1 + volumeMounts: + - mountPath: /share + name: weblogic-domain-storage-volume + + # specify a new configuration section, remove the old configuration section. + + configuration: + + # secrets that are referenced by model yaml macros + # sample-domain1-rcu-access is used for JRF domains + secrets: [ sample-domain1-rcu-access ] + + initializeDomainOnPV: + persistentVolumeClaim: + metadata: + name: sample-domain1-pvc-rwm1 + spec: + storageClassName: my-storage-class + resources: + requests: + storage: 10Gi + domain: + createIfNotExists: Domain + domainCreationImages: + - image: 'myaux:v6' + domainType: JRF + domainCreationConfigMap: sample-domain1-wdt-config-map + opss: + # Make sure you have already saved the wallet file secret. This allows the domain to use + # an existing JRF database schemas. + walletFileSecret: sample-domain1-opss-walletfile-secret + walletPasswordSecret: sample-domain1-opss-wallet-password-secret +``` + +4. Deploy the domain. If it is successful, then the domain has been migrated to a persistent volume. Now, you can proceed to upgrade to version 14.1.2.0, see [FMW/JRF domain on PV](#fmwjrf-domain-on-persistent-volume). + +### Sample WDT model for secured production mode and SSL + +If you are upgrading an existing domain to 14.1.2.0 and your existing domain does not have secured production mode enabled, the operator, by default, will _disable_ secured production mode. If you want to override this behavior, you must enable it explicitly. Optionally, you can delete the existing domain and let the operator completely rebuild the domain and, by default, secured production mode will be enabled; you do not have to enable it explicitly. + +The following is a code snippet of a WDT model for setting up secured production mode and SSL. + +``` +topology: + # Production mode must be true for secured production mode + # + ProductionModeEnabled: true + SecurityConfiguration: + # If you are updating an existing, pre 14.1.2.0 domain that does not have secure mode enabled, + # and want to use secured production mode, make sure you enable secure mode; otherwise the + # operator will disable it by default. + # + # + SecureMode: + SecureModeEnabled: true + # + # Make sure SSL is set up in all servers and server templates. + # + Server: + "admin-server": + CustomTrustKeyStoreFileName: 'wlsdeploy/servers/admin-server/trust-keystore.jks' + CustomIdentityKeyStoreFileName: 'wlsdeploy/servers/admin-server/identity-keystore.jks' + KeyStores: CustomIdentityAndCustomTrust + CustomIdentityKeyStoreType: JKS + CustomTrustKeyStoreType: JKS + CustomIdentityKeyStorePassPhraseEncrypted: + CustomTrustKeyStorePassPhraseEncrypted: + SSL: + ListenPort: 7002 + Enabled : true + ServerPrivateKeyAlias: adminkey + ServerPrivateKeyPassPhraseEncrypted: + ServerTemplate: + "cluster-1-template": + CustomTrustKeyStoreFileName: 'wlsdeploy/servers/managed-server/trust-keystore.jks' + CustomIdentityKeyStoreFileName: 'wlsdeploy/servers/managed-server/identity-keystore.jks' + KeyStores: CustomIdentityAndCustomTrust + CustomIdentityKeyStoreType: JKS + CustomTrustKeyStoreType: JKS + CustomIdentityKeyStorePassPhraseEncrypted: + CustomTrustKeyStorePassPhraseEncrypted: + SSL: + ListenPort: 7102 + Enabled : true + ServerPrivateKeyAlias: mykey + ServerPrivateKeyPassPhraseEncrypted: + +``` diff --git a/documentation/site/content/managing-domains/model-in-image/overview.md b/documentation/site/content/managing-domains/model-in-image/overview.md index 40fe61aaa3f..a464ddd595f 100644 --- a/documentation/site/content/managing-domains/model-in-image/overview.md +++ b/documentation/site/content/managing-domains/model-in-image/overview.md @@ -1,84 +1,124 @@ -+++ -title = "Overview" -date = 2020-03-11T16:45:16-05:00 -weight = 10 -pre = " " -description = "Introduction to Model in Image, description of its runtime behavior, and references." -+++ - -{{< table_of_contents >}} - -### Introduction - -Model in Image is an alternative to the operator's Domain in Image and Domain on PV domain home source types. For a comparison, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). Unlike Domain on PV and Domain in Image, Model in Image eliminates the need to pre-create your WebLogic domain home prior to deploying your Domain YAML file. - -It enables: - - - Defining a WebLogic domain home configuration using WebLogic Deploy Tooling (WDT) model files and application archives. - - Embedding these files in a single image that also contains a WebLogic installation, - and using the WebLogic Image Tool (WIT) to generate this image. Or, alternatively, - embedding the files in one or more application-specific images. - - Optionally, supplying additional model files using a Kubernetes ConfigMap. - - Supplying Kubernetes Secrets that resolve macro references within the models. - For example, a secret can be used to supply a database credential. - - Updating WDT model files at runtime. The WDT models are considered the source of truth and match the domain configuration at all times. For example, you can add a data source - to a running domain. See [Runtime updates](#runtime-updates) for details. - -This feature is supported for standard WLS domains. **For JRF domains**, use [Domain on PV]({{< relref "/managing-domains/domain-on-pv/overview.md" >}}). - -### WebLogic Deploy Tooling models - -WDT models are a convenient and simple alternative to WebLogic Scripting Tool (WLST) -configuration scripts. -They compactly define a WebLogic domain using YAML files and support including -application archives in a ZIP file. For a description of the model format -and its integration with Model in Image, -see [Usage]({{< relref "/managing-domains/model-in-image/usage.md" >}}) -and [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}). -The WDT model format is fully described in the open source, -[WebLogic Deploy Tooling](https://oracle.github.io/weblogic-deploy-tooling/) GitHub project. - -### Runtime behavior - -When you deploy a Model in Image domain resource YAML file: - - - The operator will run a Kubernetes Job called the 'introspector job' that: - - For an [Auxiliary Image]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) deployment, an init container is used to copy and set up the WDT installer in the main container, and all the WDT models are also copied to the main container. - - Sets up the call parameters for WDT to create the domain. The ordering of the models follow the pattern [Model files naming and ordering]({{< relref "/managing-domains/model-in-image/model-files#model-file-naming-and-loading-order" >}}). - - Runs WDT tooling to generate a domain home using the parameters from the previous step. - - Encrypts the domain salt key `SerializedSystemIni.dat`. - - Packages the domain home and passes it to the operator. The packaged domain has two parts. The first part `primordial domain` contains the basic configuration including the encrypted salt key. The second part `domain config` contains the rest of the configuration `config/**/*.xml`. These files are compressed but do not contain any applications, libraries, key stores, and such, because they can be restored from the WDT archives. - - - After the introspector job completes: - - The operator creates one or more ConfigMaps following the pattern `DOMAIN_UID-weblogic-domain-introspect-cm***`. These ConfigMaps contain the packaged domains from the introspector job and other information for starting the domain. - - - After completion of the introspector job, the operator will start the domain: - - For an [Auxiliary Image]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) deployment, an init container is used to copy and set up the WDT installer in the main container, and all the WDT models are also copied to the main container first. - - Restore the packaged domains in the server pod. - - Restore applications, libraries, key stores, and such, from the WDT archives. - - Decrypt the domain salt key. - - Start the domain. - -### Runtime updates - -Model updates can be applied at runtime by changing an image, secrets, a domain resource, or a WDT model ConfigMap after initial deployment. - -Some updates may be applied to a running domain without requiring any WebLogic pod restarts (an online update), -but others may require rolling the pods to propagate the update's changes (an offline update), -and still others may require shutting down the entire domain before applying the update (a full domain restart update). -_It is the administrator's responsibility to make the necessary changes to a domain resource to initiate the correct type of update._ - -See [Runtime updates]({{< relref "/managing-domains/model-in-image/runtime-updates.md" >}}). - -### Continuous integration and delivery (CI/CD) - -To understand how Model in Image works with CI/CD, see [CI/CD considerations]({{< relref "/managing-domains/cicd/_index.md" >}}). - -### References - - - [Model in Image sample]({{< relref "/samples/domains/model-in-image/_index.md" >}}) - - [WebLogic Deploy Tooling (WDT)](https://oracle.github.io/weblogic-deploy-tooling/) - - [WebLogic Image Tool (WIT)](https://oracle.github.io/weblogic-image-tool/) - - Domain [schema](https://github.com/oracle/weblogic-kubernetes-operator/blob/{{< latestMinorVersion >}}/documentation/domains/Domain.md), [documentation]({{< relref "/managing-domains/domain-resource.md" >}}) - - HTTP load balancers: Ingress [documentation]({{< relref "/managing-domains/accessing-the-domain/ingress/_index.md" >}}), [sample]({{< relref "/samples/ingress/_index.md" >}}) - - [CI/CD considerations]({{< relref "/managing-domains/cicd/_index.md" >}}) ++++ +title = "Overview" +date = 2020-03-11T16:45:16-05:00 +weight = 10 +pre = " " +description = "Introduction to Model in Image, description of its runtime behavior, and references." ++++ + +{{< table_of_contents >}} + +### Introduction + +Model in Image is an alternative to the operator's Domain in Image and Domain on PV domain home source types. For a comparison, see [Choose a domain home source type]({{< relref "/managing-domains/choosing-a-model/_index.md" >}}). Unlike Domain on PV and Domain in Image, Model in Image eliminates the need to pre-create your WebLogic domain home prior to deploying your Domain YAML file. + +It enables: + + - Defining a WebLogic domain home configuration using WebLogic Deploy Tooling (WDT) model files and application archives. + - Embedding these files in a single image that also contains a WebLogic installation, + and using the WebLogic Image Tool (WIT) to generate this image. Or, alternatively, + embedding the files in one or more application-specific images. + - Optionally, supplying additional model files using a Kubernetes ConfigMap. + - Supplying Kubernetes Secrets that resolve macro references within the models. + For example, a secret can be used to supply a database credential. + - Updating WDT model files at runtime. The WDT models are considered the source of truth and match the domain configuration at all times. For example, you can add a data source + to a running domain. See [Runtime updates](#runtime-updates) for details. + +This feature is supported for standard WLS domains. **For JRF domains**, use [Domain on PV]({{< relref "/managing-domains/domain-on-pv/overview.md" >}}). + +### WebLogic Deploy Tooling models + +WDT models are a convenient and simple alternative to WebLogic Scripting Tool (WLST) +configuration scripts. +They compactly define a WebLogic domain using YAML files and support including +application archives in a ZIP file. For a description of the model format +and its integration with Model in Image, +see [Usage]({{< relref "/managing-domains/model-in-image/usage.md" >}}) +and [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}). +The WDT model format is fully described in the open source, +[WebLogic Deploy Tooling](https://oracle.github.io/weblogic-deploy-tooling/) GitHub project. + +### Runtime behavior + +When you deploy a Model in Image domain resource YAML file: + + - The operator will run a Kubernetes Job called the 'introspector job' that: + - For an [Auxiliary Image]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) deployment, an init container is used to copy and set up the WDT installer in the main container, and all the WDT models are also copied to the main container. + - Sets up the call parameters for WDT to create the domain. The ordering of the models follow the pattern [Model files naming and ordering]({{< relref "/managing-domains/model-in-image/model-files#model-file-naming-and-loading-order" >}}). + - Runs WDT tooling to generate a domain home using the parameters from the previous step. + - Encrypts the domain salt key `SerializedSystemIni.dat`. + - Packages the domain home and passes it to the operator. The packaged domain has two parts. The first part `primordial domain` contains the basic configuration including the encrypted salt key. The second part `domain config` contains the rest of the configuration `config/**/*.xml`. These files are compressed but do not contain any applications, libraries, key stores, and such, because they can be restored from the WDT archives. + + - After the introspector job completes: + - The operator creates one or more ConfigMaps following the pattern `DOMAIN_UID-weblogic-domain-introspect-cm***`. These ConfigMaps contain the packaged domains from the introspector job and other information for starting the domain. + + - After completion of the introspector job, the operator will start the domain: + - For an [Auxiliary Image]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) deployment, an init container is used to copy and set up the WDT installer in the main container, and all the WDT models are also copied to the main container first. + - Restore the packaged domains in the server pod. + - Restore applications, libraries, key stores, and such, from the WDT archives. + - Decrypt the domain salt key. + - Start the domain. + + +### Using demo SSL certificates in v14.1.2.0.0 or later + +{{% notice note %}} +Beginning with WebLogic Server version 14.1.2.0.0, when a domain is `production` mode enabled, it is automatically `secure mode` enabled, therefore, all communications with the domain are using SSL channels and non-secure listening ports are disabled. If there are no custom certificates configured for the SSL channels, then the server uses the demo SSL certificates. +The demo SSL certificates are now domain specific and generated when the domain is first created, +unlike previous releases, which were distributed with the WebLogic product installation. Oracle recommends using custom SSL +certificates in a production environment. +{{% /notice %}} + +The certificates are created under the domain home `security` folder. + +``` +-rw-r----- 1 oracle oracle 1275 Feb 15 15:55 democakey.der +-rw-r----- 1 oracle oracle 1070 Feb 15 15:55 democacert.der +-rw-r----- 1 oracle oracle 1478 Feb 15 15:55 DemoTrust.p12 +-rw-r----- 1 oracle oracle 1267 Feb 15 15:55 demokey.der +-rw-r----- 1 oracle oracle 1099 Feb 15 15:55 democert.der +-rw-r----- 1 oracle oracle 1144 Feb 15 15:55 DemoCerts.props +-rw-r----- 1 oracle oracle 2948 Feb 15 15:55 DemoIdentity.p12 +``` + +For Model in Image domains, whenever you change any security credentials including, but not limited to, the Administration Server credentials, RCU credentials, and such, the domain will be recreated and a new set of demo SSL certificates will be generated. The SSL certificates are valid for 6 months, then they expire. + +The demo CA certificate expires in 5 years, however, whenever the domain is recreated, the entire set of certificates are regenerated so you _must_ import the demo CA certificate again. + +If you have any external client that needs to communicate with WebLogic Servers using SSL, then you need to import the current self-signing CA certificate, `democacert.der`, +into your local trust store. + +```shell + keytool -importcert -keystore -alias wlscacert -file $HOME/Downloads/democacer.der +``` + +If you are using the WebLogic Scripting Tool, before starting the WLST session, you can set the following system properties. + +```shell + export WLST_PROPERTIES="-Dweblogic.security.TrustKeyStore=DemoTrust -Dweblogic.security.SSL.ignoreHostnameVerification=true" +``` + + +### Runtime updates + +Model updates can be applied at runtime by changing an image, secrets, a domain resource, or a WDT model ConfigMap after initial deployment. + +Some updates may be applied to a running domain without requiring any WebLogic pod restarts (an online update), +but others may require rolling the pods to propagate the update's changes (an offline update), +and still others may require shutting down the entire domain before applying the update (a full domain restart update). +_It is the administrator's responsibility to make the necessary changes to a domain resource to initiate the correct type of update._ + +See [Runtime updates]({{< relref "/managing-domains/model-in-image/runtime-updates.md" >}}). + +### Continuous integration and delivery (CI/CD) + +To understand how Model in Image works with CI/CD, see [CI/CD considerations]({{< relref "/managing-domains/cicd/_index.md" >}}). + +### References + + - [Model in Image sample]({{< relref "/samples/domains/model-in-image/_index.md" >}}) + - [WebLogic Deploy Tooling (WDT)](https://oracle.github.io/weblogic-deploy-tooling/) + - [WebLogic Image Tool (WIT)](https://oracle.github.io/weblogic-image-tool/) + - Domain [schema](https://github.com/oracle/weblogic-kubernetes-operator/blob/{{< latestMinorVersion >}}/documentation/domains/Domain.md), [documentation]({{< relref "/managing-domains/domain-resource.md" >}}) + - HTTP load balancers: Ingress [documentation]({{< relref "/managing-domains/accessing-the-domain/ingress/_index.md" >}}), [sample]({{< relref "/samples/ingress/_index.md" >}}) + - [CI/CD considerations]({{< relref "/managing-domains/cicd/_index.md" >}}) diff --git a/documentation/site/content/managing-domains/model-in-image/runtime-updates.md b/documentation/site/content/managing-domains/model-in-image/runtime-updates.md index 38943cc0994..51d57925a05 100644 --- a/documentation/site/content/managing-domains/model-in-image/runtime-updates.md +++ b/documentation/site/content/managing-domains/model-in-image/runtime-updates.md @@ -1,813 +1,815 @@ -+++ -title = "Runtime updates" -date = 2020-03-11T16:45:16-05:00 -weight = 50 -pre = " " -description = "Updating a running Model in Image domain's images and model files." -+++ - - -{{< table_of_contents >}} - -### Overview - -If you want to make a WebLogic domain home configuration update to a running Model in Image domain, -and you want the update to survive WebLogic Server pod restarts, -then you must modify your existing model and instruct the WebLogic Kubernetes Operator to propagate the change. - -If instead you make a direct runtime WebLogic configuration update of a Model in Image domain -using the WebLogic Server Administration Console or WLST scripts, -then the update will be ephemeral. -This is because a Model in Image domain home is regenerated from the model on every pod restart. - -There are two approaches for propagating model updates to a running Model in Image domain -without first shutting down the domain: - -- _Offline updates_: [Offline updates](#offline-updates) are propagated to WebLogic pods by updating your model - and then initiating a domain roll, which generates a new domain configuration, - restarts the domain's WebLogic Server Administration Server with the updated configuration, - and then restarts other running servers. - - - _Online updates_: If model changes are configured to fully dynamic configuration MBean attributes, - then you can optionally propagate changes to WebLogic pods without a roll using an [online update](#online-updates). - If an online update request includes non-dynamic model updates that can only be achieved - using an offline update, - then the resulting behavior is controlled by the domain resource YAML - `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` attribute, - which is described in detail in [Online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). - -The operator does not support all types of WebLogic configuration changes while a domain is still running. -If a change is unsupported for an online or offline update, then propagating -the change requires entirely shutting domain the domain, -applying the change, and finally restarting the domain. Full domain restarts are described in -[Full domain restarts]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#full-domain-restarts">}}). - -**NOTE**: Supported and unsupported changes are described in these sections: [Supported updates](#supported-updates) and [Unsupported updates](#unsupported-updates). -_It is the administrator's responsibility to make the necessary changes to a domain resource to initiate the correct approach for an update._ - -{{% notice note %}} -Custom configuration overrides, which are WebLogic configuration overrides -specified using a domain resource YAML file `configuration.overridesConfigMap`, as described in -[Configuration overrides]({{< relref "/managing-domains/configoverrides/_index.md" >}}), -are _not_ supported in combination with Model in Image. -Model in Image will generate an error if custom overrides are specified. -This should not be a concern because model file, secret, or model image updates are simpler -and more flexible than custom configuration override updates. -Unlike configuration overrides, the syntax for a model file update exactly matches -the syntax for specifying your model file originally. -{{% /notice %}} - -### Updating an existing model - -If you have verified your proposed model updates to a running -Model in Image domain are supported by consulting -[Supported updates](#supported-updates) -and -[Unsupported updates](#unsupported-updates), -then you can use the following approaches. - -For online or offline updates: - - - Specify a new or changed WDT ConfigMap that contains model files - and use your domain resource YAML file `configuration.model.configMap` field to reference the map. - The model files in the ConfigMap will be merged with any model files in the image. - Ensure the ConfigMap is deployed to the same namespace as your domain. - - - Change, add, or delete secrets that are referenced by macros in your model files - and use your domain resource YAML file `configuration.secrets` field to reference the secrets. - Ensure the secrets are deployed to the same namespace as your domain. - -For offline updates only, there are two additional options: - - - Supply a new image with new or changed model files. - - If the files are located in the image specified in the domain resource YAML file `spec.image`, - then change this field to reference the image. - - If you are using - [auxiliary images]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) - to supply - model files in an image, then change the corresponding `serverPod.auxiliaryImages.image` field - value to reference the new image or add a new `serverPod.auxiliaryImages` mount for - the new image. - - - Change, add, or delete environment variables that are referenced by macros in your model files. - Environment variables are specified in the domain resource YAML file `spec.serverPod.env` - or `spec.serverPod.adminServer.env` attributes. - -{{% notice tip %}} -It is advisable to defer the last two modification options, or similar domain resource YAML file changes to -[fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}), -until all of your other modifications are ready. -This is because such changes automatically and immediately result in a rerun of your introspector job, -and, if the job succeeds, then a roll of the domain, -plus, an offline update, if there are any accompanying model changes. -{{% /notice %}} - -Model updates can include additions, changes, and deletions. For help generating model changes: - - - For a description of model file syntax, see the - [WebLogic Deploy Tooling](https://oracle.github.io/weblogic-deploy-tooling/) documentation - and Model in Image [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}) documentation. - - - For a description of helper tooling that you can use to generate model change YAML, - see [Using the WDT Discover and Compare Model Tools](#using-the-wdt-discover-domain-and-compare-model-tools). - - - If you specify multiple model files in your image, volumes (including those based on images from the [auxiliary images]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) feature), or WDT ConfigMap, - then the order in which they're loaded and merged is determined as described in - [Model file naming and loading order]({{< relref "/managing-domains/model-in-image/model-files/_index.md#model-file-naming-and-loading-order" >}}). - - - If you are performing an online update and the update includes deletes, then - see [Online update handling of deletes](#online-update-handling-of-deletes). - -After your model updates are prepared, you can instruct the operator to propagate the changed model -to a running domain by following the steps in [Offline updates](#offline-updates) -or [Online updates](#online-updates). - -### Offline updates - -Use the following steps to initiate an offline configuration update to your model: - - 1. Ensure your updates are supported by checking [Supported](#supported-updates) and [Unsupported](#unsupported-updates) updates. - 1. Modify, add, or delete your model resources as per [Updating an existing model](#updating-an-existing-model). - 1. Modify your domain resource YAML file: - 1. If you have updated your image: - - If the files are located in the image specified in the domain resource YAML file `spec.image`, - then change this field to reference the image. - - If you are using - [auxiliary images]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) - to supply - model files in an image, then change the corresponding `serverPod.auxiliaryImages.image` field - value to reference the new image or add a new `serverPod.auxiliaryImages` mount for - the new image. - 1. If you are updating environment variables, change `domain.spec.serverPod.env` - or `domain.spec.adminServer.serverPod.env` accordingly. - 1. If you are specifying a WDT ConfigMap, then set `domain.spec.configuration.model.configMap` - to the name of the ConfigMap. - 1. If you are adding or deleting secrets as part of your change, then ensure - the `domain.spec.configuration.secrets` array reflects all current secrets. - 1. If you have modified your image or environment variables, - then no more domain resource YAML file changes are needed; - otherwise, change an attribute that instructs the operator to roll the domain. - For examples, see - [change the domain `spec.restartVersion`](#changing-a-domain-restartversion-or-introspectversion) - or change any of the other Domain resource YAML [fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}). - -The operator will subsequently rerun the domain's introspector job. -This job will reload all of your secrets and environment variables, -merge all of your model files, and generate a new domain home. - -If the job succeeds, then the operator will make the updated domain home available to pods -using a ConfigMap named `DOMAIN_UID-weblogic-domain-introspect-cm` and the operator will -subsequently roll (restart) each running WebLogic Server pod in the domain -so that it can load the new configuration. -A domain roll begins by restarting the domain's Administration Server and -then proceeds to restart each Managed Server in the domain. - -If the job reports a failure, see -[Debugging]({{< relref "/managing-domains/debugging.md" >}}) -for advice. - -#### Offline update sample - -For an offline update sample which adds a data source, see the -[Update 1 use case]({{< relref "/samples/domains/model-in-image/update1.md" >}}) -in the Model in Image sample. - -### Online updates - -Use the following steps to initiate an online configuration update to your model: - - 1. Ensure your updates are supported by checking [Supported](#supported-updates) and [Unsupported](#unsupported-updates) updates. - 1. Modify, add, or delete your model secrets or WDT ConfigMap - as per [Updating an existing model](#updating-an-existing-model). - 1. Modify your domain resource YAML file: - 1. **Do not** change `domain.spec.image`, `domain.spec.serverPod.env`, or any other domain resource YAML - [fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}); - this will automatically and immediately result in a rerun of your introspector job, - a roll if the job succeeds, plus an offline update if there are any accompanying model changes. - 1. If you are specifying a WDT ConfigMap, then set `domain.spec.configuration.model.configMap` - to the name of the ConfigMap. - 1. If you are adding or deleting secrets as part of your change, then ensure the - `domain.spec.configuration.secrets` array reflects all current secrets. - 1. Set `domain.spec.configuration.model.onlineUpdate.enabled` to `true` (default is `false`). - 1. Set `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` to one of - `CommitUpdateOnly` (default), and `CommitUpdateAndRoll`. - For details, see - [online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). - - 1. Optionally, tune the WDT timeouts in `domain.spec.configuration.model.onlineUpdate.wdtTimeouts`. - - This is only necessary in the rare case when an introspector job's WDT online update command timeout - results in an error in the introspector job log or operator log. - - All timeouts are specified in milliseconds and default to two or three minutes. - - For a full list of timeouts, you can call - `kubectl explain domain.spec.configuration.model.onlineUpdate.wdtTimeouts`. - 1. Change `domain.spec.introspectVersion` to a different value. For examples, see - [change the domain `spec.introspectVersion`](#changing-a-domain-restartversion-or-introspectversion). - -After you've completed these steps, the operator will subsequently run an introspector Job which -generates a new merged model, -compares the new merged model to the previously deployed merged model, -and runs WebLogic Deploy Tooling to process the differences: - - - If the introspector job WDT determines that the differences are confined to - fully dynamic WebLogic configuration MBean changes, - then the operator will send delta online updates to the running WebLogic pods. - - - If WDT detects non-dynamic WebLogic configuration MBean changes, then the operator may ignore the updates, - honor only the online updates, or initiate an offline update (roll) depending on whether you have configured - `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` to `CommitUpdateOnly` (default), or - `CommitUpdateAndRoll`. - For details, see - [online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). - - -If the introspector job reports a failure or any other failure occurs, then -see [Debugging]({{< relref "/managing-domains/debugging.md" >}}) for advice. -When recovering from a failure, please keep the following points in mind: - - - The operator cannot automatically revert changes to resources that are under - user control (just like with offline updates). For example, it is the administrator's - responsibility to revert problem changes to an image, configMap, secrets, and domain resource YAML file. - - - If there is any failure during an online update, then no WebLogic configuration changes - are made to the running domain and the introspector job retries up to the failure retry time - limit specified in `domain.spec.failureRetryLimitMinutes`. - To correct the problem, modify and reapply your model resources (ConfigMap and/or secrets), - plus, if the introspector job has stopped retrying, you must also change your domain resource - `domain.spec.introspectVersion` again. For more information, see [Domain failure retry processing]({{< relref "/managing-domains/domain-lifecycle/retry.md" >}}). - - - -Sample domain resource YAML file for an online update: - -```yaml -... -kind: Domain -metadata: - name: sample-domain1 - namespace: sample-domain1-ns -... -spec: - ... - introspectVersion: 5 - configuration: - ... - model: - domainType: "WLS" - configMap: sample-domain1-wdt-config-map - runtimeEncryptionSecret: sample-domain1-runtime-encryption-secret - onlineUpdate: - enabled: true - onNonDynamicChanges: "CommitUpdateAndRoll" - secrets: - - sample-domain1-datasource-secret - - sample-domain1-another-secret -``` - -#### Online update scenarios - -1. Successful online update that includes only dynamic WebLogic MBean changes. - * Example dynamic WebLogic MBean changes: - * Changing data source connection pool capacity, password, and targets. - * Changing application targets. - * Deleting or adding a data source. - * Deleting or adding an application. - * The MBean changes are committed in the running domain and effective immediately. - * Expected outcome after the introspector job completes: - * The domain `Completed` condition status is set to `True`. - For more information, see [Domain conditions]({{< relref "/managing-domains/accessing-the-domain/status-conditions#types-of-domain-conditions" >}}). - * The `weblogic.introspectVersion` label on all pods will be set to match the `domain.spec.introspectVersion`. - * Actions required: - * None. - -1. Successful online update that includes non-dynamic WebLogic MBean attribute changes when `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateOnly` (the default). - * Example non-dynamic WebLogic MBean change: - * Changing a data source driver parameter property (such as `username`). - * Expected outcome after the introspector job completes: - * Any dynamic WebLogic configuration changes are committed in the running domain and effective immediately. - * Non-dynamic WebLogic configuration changes will not take effect on already running WebLogic Server pods until an administrator subsequently rolls the pod. - * The domain status `Available` condition will have a `Status` of `True`. - * The domain status `ConfigChangesPendingRestart` condition will have a `Status` of `True` until an administrator subsequently rolls all WebLogic Server pods that are already running. - * Each WebLogic Server pod's `weblogic.introspectVersion` label will match `domain.spec.introspectVersion`. - * Each WebLogic Server pod that is already running will be given a `weblogic.configChangesPendingRestart=true` label until an administrator subsequently rolls the pod. - * Actions required: - * If you want the non-dynamic changes to take effect, then restart the pod(s) with the `weblogic.configChangesPendingRestart=true` label (such as by initiating a domain roll). - * See [Online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). - -1. Successful online update that includes non-dynamic WebLogic MBean attribute changes when `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateAndRoll`. - * Expected outcome after the introspector job completes: - * Any dynamic WebLogic configuration changes are committed in the running domain and effective immediately. - * The operator will initiate a domain roll. - * Non-dynamic WebLogic configuration changes will take effect on each pod when the pod is rolled. - * Each WebLogic Server pod's `weblogic.introspectVersion` label will match `domain.spec.introspectVersion` after it is rolled. - * The domain status `Available` condition will have a `Status` of `True` after the roll completes. - * Actions required: - * None. All changes will complete after the operator initiated domain roll completes. - * See [Online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). - -1. Changing any of the domain resource [fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}) in addition to `domain.spec.introspectVersion`, `spec.configuration.secrets`, `spec.configuration.model.onlineUpdate`, or `spec.configuration.model.configMap`. - * Expected outcome after the introspector job completes: - * No online update was attempted by the introspector job. - * All model changes are treated the same as offline updates (which may result in restarts/roll after job success). - * Actions required: - * None. - -1. Changing any model attribute that is [unsupported](#unsupported-updates). - * Expected outcome: - * The expected behavior is often undefined, but in some cases there will be helpful error in the introspector job, events, and/or domain status, and the job will periodically retry until the error is corrected or its maximum error count exceeded. - * Actions required: - * Use offline updates if they are supported, or, if not, shutdown the entire domain and restart it. - * See [Debugging]({{< relref "/managing-domains/debugging.md" >}}). - -1. Errors in the model; for example, a syntax error. - * Expected outcome after the introspector job completes: - * Error in the introspector job's pod log, domain events, and domain status. - * The domain status `Failed` condition will have a `Status` of `True`. - * Periodic job retries until the error is corrected or until a maximum error count is exceeded. - * Actions required: - * Correct the model. - * If retries have halted, then alter the `spec.introspectVersion`. - -1. Other errors while updating the domain. - * Expected outcome: - * Error in the introspector job, domain events, and/or domain status. - * The domain status `Failed` condition will have a `Status` of `True`. - * If there's a failed introspector job, the job will retry periodically until the error is corrected or until it exceeds its maximum error count. Other types of errors will also usually incur periodic retries. - * Actions required: - * See [Debugging]({{< relref "/managing-domains/debugging.md" >}}). - * Make corrections to the domain resource and/or model. - * If retries have halted, then alter the `spec.introspectVersion`. - - -#### Online update status and labels - -During an online update, the operator will rerun the introspector job, which -in turn attempts online WebLogic configuration changes to the running domain. -You can monitor an update's status using its domain resource's status conditions -and its WebLogic Server pod labels. - -For example, for the domain status -you can check the domain resource `domain.status` stanza -using `kubectl -n MY_NAMESPACE get domain MY_DOMAINUID -o yaml`, -and for the WebLogic pod labels you can use -`kubectl -n MY_NAMESPACE get pods --show-labels` plus -optionally add `--watch` to watch the pods as they change over time. - -The `ConfigChangesPendingRestart` condition in `domain.status` contains information about the progress -of the online update. See [ConfigChangesPendingRestart condition]({{< relref "/managing-domains/accessing-the-domain/status-conditions#configchangespendingrestart" >}}) for details. - -Here are some of the expected WebLogic pod labels after an online update success: - - 1. Each WebLogic Server pod's `weblogic.introspectVersion` label value - will eventually match the `domain.spec.introspectVersion` value that you defined. - * If the domain resource attribute - `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateOnly` (the default), - then the introspect version label on all pods is immediately updated - after the introspection job successfully completes. - * If the domain resource attribute - `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateAndRoll` - and there are no non-dynamic configuration changes to the model, - then the introspect version label on all pods is immediately updated - after the introspection job successfully completes. - * If the domain resource attribute - `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateAndRoll` - and there are non-dynamic clabel onfiguration changes to the model, - then the introspect version label on each pod is updated after the pod is rolled. - - 1. There will be a `weblogic.configChangesPendingRestart=true` label on each - WebLogic Server pod until the pod is restarted (rolled) by an administrator - if all of the following are true: - * The domain resource `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` - attribute is `CommitUpdateOnly` (the default). - * Non-dynamic WebLogic configuration changes were included in a - successful online model update. - -#### Online update handling of non-dynamic WebLogic configuration changes - -The domain resource YAML `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` attribute -controls behavior when non-dynamic WebLogic configuration changes are detected during an online update introspector job. -Non-dynamic changes are changes that require a domain restart to take effect. -Valid values are `CommitUpdateOnly` (default), or `CommitUpdateAndRoll`: - -* If set to `CommitUpdateOnly` (the default) and any non-dynamic changes are detected, - then all changes will be committed, - dynamic changes will take effect immediately, - the domain will not automatically restart (roll), - and any non-dynamic changes will become effective on a pod only when - the pod is later restarted. - -* If set to `CommitUpdateAndRoll` and any non-dynamic changes are detected, - then all changes will be committed, dynamic changes will take effect immediately, - the domain will automatically restart (roll), - and non-dynamic changes will take effect on each pod after the pod restarts. - -{{% notice note %}} - When updating a domain with non-dynamic MBean changes with - `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges=CommitUpdateOnly` (the default), - the non-dynamic changes are not effective on a WebLogic pod until the pod is restarted. - However, if you scale up a cluster or otherwise start any new servers in the domain, - then the new servers will start with the new non-dynamic changes - and the domain will then be running in an inconsistent state until its older servers are restarted. -{{% /notice %}} - -#### Online update handling of deletes - -The primary use case for online updates is to make small additions, - deletions of single resources or MBeans that have no dependencies, - or changes to non-dynamic MBean attributes. - -Deletion can be problematic for online updates in two cases: -- Deleting multiple resources that have cross dependencies. -- Deleting the parent type section in an MBean hierarchy. - -In general, complex deletion should be handled by offline updates - to avoid these problems. - -**NOTE**: Implicitly removing a model's parent type - section may sometimes work depending - on the type of the section. For example, if you have an application - in the model under `appDeployments:` in a `model.configMap` and you - subsequently update the ConfigMap using an online update so that it - no longer includes the `appDeployment` section, then the online update - will delete the application from the domain. - -##### MBean type section deletion - -For an example of an MBean deletion, consider a WDT ConfigMap that starts with: - -```yaml - resources: - SelfTuning: - WorkManager: - wm1: - Target: 'cluster-1' - wm2: - Target: 'cluster-1' - JDBCSystemResource: - ... -``` - -If you want to online update to a new model without `work-managers`, - then change the ConfigMap to the following: - -```yaml - resources: - SelfTuning: - WorkManager: - JDBCSystemResource: - ... -``` - -Or, supply an additional ConfigMap: - -```yaml - resources: - SelfTuning: - WorkManager: - '!wm1': - '!wm2': -``` - -The online update will fail if you try replace the ConfigMap - with the `SelfTuning` section omitted: - -```yaml - resources: - JDBCSystemResource: - ... -``` - -This fails because it implicitly removes - the MBean types `SelfTuning` and `WorkManager`. - -##### Deleting cross-referenced MBeans - -For an example of an unsupported online update delete of MBeans - with cross references, consider the case of a Work Manager - configured with constraints where you want to delete the entire Work Manager: - -```yaml - resources: - SelfTuning: - WorkManager: - newWM: - Target: 'cluster-1' - MinThreadsConstraint: 'SampleMinThreads' - MaxThreadsConstraint: 'SampleMaxThreads' - MinThreadsConstraint: - SampleMinThreads: - Count: 1 - MaxThreadsConstraint: - SampleMaxThreads: - Count: 10 -``` - -If you try to specify the updated model in the ConfigMap as: - -```yaml - resources: - SelfTuning: - WorkManager: - MinThreadsConstraint: - MaxThreadsConstraint: - -``` - -Then, the operator will try use this delta to online update the domain: - -```yaml - resources: - SelfTuning: - MaxThreadsConstraint: - '!SampleMaxThreads': - WorkManager: - '!newWM': - MinThreadsConstraint: - '!SampleMinThreads': -``` - -This can fail because an online update might not delete all the referenced `Constraints` first - before deleting the `WorkManager`. - -To work around problems with online updates to objects with cross dependencies, you can - use a series of online updates to make the change in stages. For example, continuing the previous Work Manager - example, first perform an online update to omit the Work Manager but not the constraints: - -```yaml - resources: - SelfTuning: - WorkManager: - MinThreadsConstraint: - SampleMinThreads: - Count: 1 - MaxThreadsConstraint: - SampleMaxThreads: - Count: 10 -``` - -After that update completes, then perform another online update: - -```yaml - resources: - SelfTuning: - WorkManager: - MinThreadsConstraint: - MaxThreadsConstraint: -``` - -#### Online update sample - -For an online update sample which alters a data source and Work Manager, see the -[Update 4 use case]({{< relref "/samples/domains/model-in-image/update4.md" >}}) -in the Model in Image sample. - -### Appendices - -Review the following appendices for additional, important information. - -#### Supported updates - -The following updates are *supported* for offline or online updates, -except when they reference an area that is specifically -documented as [unsupported](#unsupported-updates): - - - You can add a new WebLogic cluster or standalone server. - - - You can increase the size of a dynamic WebLogic cluster. - - - You can add new MBeans or resources by specifying their corresponding model YAML file snippet - along with their parent bean hierarchy. For example, you can add a data source. - - - You can change or add MBean attributes by specifying a YAML file snippet - along with its parent bean hierarchy that references an existing MBean and the attribute. - For example, to add or alter the maximum capacity of a data source named `mynewdatasource`: - - ```yaml - resources: - JDBCSystemResource: - mynewdatasource: - JdbcResource: - JDBCConnectionPoolParams: - MaxCapacity: 5 - ``` - - For more information, see [Using Multiple Models](https://oracle.github.io/weblogic-deploy-tooling/concepts/model/#using-multiple-models) in the WebLogic Deploy Tooling documentation. - - - You can change or add secrets that your model macros reference - (macros that use the `@@SECRET:secretname:secretkey@@` syntax). - For example, you can change a database password secret. - - - For offline updates only, you can change or add environment variables - that your model macros reference - (macros that use the `@@ENV:myenvvar@@` syntax). - - - You can remove an MBean, application deployment, or resource by omitting any - reference to it in your image model files and WDT config map. - You can also remove a named MBean, application deployment, or resource - by specifying an additional model file with an exclamation point (`!`) - just before its name plus ensuring the new model file is loaded _after_ - the original model file that contains the original named configuration. - For example, if you have a data source named `mynewdatasource` defined - in your model, then it can be removed by specifying a small model file that - loads after the model file that defines the data source, where the - small model file looks like this: - - ```yaml - resources: - JDBCSystemResource: - !mynewdatasource: - ``` - There are [some exceptions for online updates](#online-update-handling-of-deletes). - - For more information, see - [Declaring Named MBeans to Delete](https://oracle.github.io/weblogic-deploy-tooling/concepts/model/#declaring-named-mbeans-to-delete) - in the WebLogic Deploying Tooling documentation. - -#### Unsupported updates - -{{% notice warning %}} -It is important to avoid applying unsupported model updates to a running domain. An attempt to use an unsupported update may not always result in a clear error message, and the expected behavior may be undefined. If you need to make an unsupported update and no workaround is documented, then shut down your domain entirely before making the change. See [Full domain restarts]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#full-domain-restarts">}}). -{{% /notice %}} - -The following summarizes the types of runtime update configuration that are _not_ supported in Model in Image unless a workaround or alternative is documented: - - - Altering cluster size: - - You have a limited ability to change an existing WebLogic cluster's membership. - - Specifically, do _not_ apply runtime updates for: - - Adding WebLogic Servers to a configured cluster. As an alternative, consider using dynamic clusters instead of configured clusters. - - Removing WebLogic Servers from a configured cluster. As an alternative, you can lower your cluster's domain resource YAML `replicas` attribute. - - Decreasing the size of a dynamic cluster. As an alternative, you can lower your cluster's domain resource YAML `replicas` attribute. - - You cannot change, add, or remove network listen address, port, protocol, and enabled configuration for existing clusters or servers at runtime. - - Specifically, do not apply runtime updates for: - - A Default, SSL, Admin channel `Enabled`, listen address, or port. - - A Network Access Point (custom channel) `Enabled`, listen address, protocol, or port. - - Note that it is permitted to override network access point `public` or `external` addresses and ports. - External access to JMX (MBean) or online WLST requires that the network access point internal port - and external port match (external T3 or HTTP tunneling access to JMS, RMI, or EJBs don't require port matching). - - {{% notice warning %}} - Due to security considerations, we strongly recommend that T3 or any RMI protocol should not be exposed outside the cluster. - {{% /notice %}} - - - - Node Manager related configuration. - - Log related settings. This applies to changing, adding, or removing server and domain log related settings in an MBean at runtime - when the domain resource is configured to override the same MBeans using the `spec.logHome`, - `spec.logHomeEnabled`, or `spec.httpAccessLogInLogHome` attributes. - - Changing the domain name. You cannot change the domain name at runtime. - - Deleting an MBean attribute: - - There is no way to directly delete an attribute from an MBean that's already been specified by a model file. - - The workaround is to do this using two model files: - - Add a model file that deletes the named bean/resource that is a parent to - the attribute you want to delete using the `!` syntax as described in [Supported Updates](#supported-updates). - - Add another model file that will be loaded after the first one, - which fully defines the named bean/resource but without the attribute you want to delete. - - Changing any existing MBean name: - - There is no way to directly change the MBean name of an attribute. - - Instead, you can remove a named MBean using the `!` syntax as described in [Supported Updates](#supported-updates). - - Then, you add a new one as a replacement. - - Embedded LDAP entries: - - Embedded LDAP security entries for [users, groups, roles](https://oracle.github.io/weblogic-deploy-tooling/samples/usersgroups-model/), - and [credential mappings](https://oracle.github.io/weblogic-deploy-tooling/samples/pwcredentialmap-model/). For example, you cannot add a user to the default security realm. - - Online update attempts in this area will fail during the introspector job, and offline update attempts may result in inconsistent security checks during the offline update's rolling cycle. - - If you need to make these kinds of updates, then shut down your domain entirely before making the change, - or switch to an [external security provider](https://oracle.github.io/weblogic-deploy-tooling/samples/securityproviders-model/). - - Any Model YAML `topology:` stanza changes: - - For example, `ConsoleEnabled`, `RootDirectory`, `AdminServerName`, and such. - - For a complete list, run `/u01/wdt/weblogic-deploy/bin/modelHelp.sh -oracle_home $ORACLE_HOME topology` - (this assumes you have installed WDT in the `/u01/wdt/weblogic-deploy` directory). - - Dependency deletion in combination with online updates. - - Deleting Model entries by type: - - For example, you cannot delete an entire `SelfTuning` type stanza - by omitting the stanza in an online update or by specifying an additional model with `!SelfTuning` - in either an offline or an online update. - - Instead, you can delete the specific MBean by omitting the MBean itself while leaving its `SelfTuning` - parent in place or by specifying an additional model using the `!` syntax in combination - with the name of the specific MBean. - - For details, see [Online update handling of deletes](#online-update-handling-of-deletes). - - Deleting multiple resources that have cross-references in combination with online updates: - - For example, concurrently deleting a persistent store and a data source referenced by the persistent store. - - For this type of failure, the introspection job will fail and log an error describing - the failed reference, and the job will automatically retry up to its maximum retries. - - For details, see [Online update handling of deletes](#online-update-handling-of-deletes). - - Security related changes in combination with online updates: - - Such changes included security changes in `domainInfo.Admin*`, - `domainInfo.RCUDbinfo.*`, `topology.Security.*`, and `topology.SecurityConfiguration.*`. - - Any online update changes in these sections will result in a failure. - -#### Using the WDT Discover Domain and Compare Model Tools - -Optionally, you can use the WDT Discover Domain and Compare Domain Tools to help generate your model file updates. -The WebLogic Deploy Tooling -[Discover Domain Tool](https://oracle.github.io/weblogic-deploy-tooling/userguide/tools/discover/) -generates model files from an existing domain home, -and its [Compare Model Tool](https://oracle.github.io/weblogic-deploy-tooling/userguide/tools/compare/) -compares two domain models and generates the YAML file for updating the first domain to the second domain. - -For example, assuming you've installed WDT in `/u01/wdt/weblogic-deploy` and assuming your domain type is `WLS`: - -1. Run discover for your existing domain home. - - ```shell - $ /u01/wdt/weblogic-deploy/bin/discoverDomain.sh \ - -oracle_home $ORACLE_HOME \ - -domain_home $DOMAIN_HOME \ - -domain_type WLS \ - -archive_file old.zip \ - -model_file old.yaml \ - -variable_file old.properties - ``` - -1. Now make some WebLogic config changes using the console or WLST. - -1. Run discover for your changed domain home. - - ```shell - $ /u01/wdt/weblogic-deploy/bin/discoverDomain.sh \ - -oracle_home $ORACLE_HOME \ - -domain_home $DOMAIN_HOME \ - -domain_type WLS \ - -archive_file new.zip \ - -model_file new.yaml \ - -variable_file new.properties - ``` -1. Compare your old and new YAML using `diff`. - - ```shell - $ diff new.yaml old.yaml - ``` -1. Compare your old and new YAML using compareDomain to generate the YAML update file you can use for transforming the old to new. - ``` - $ /u01/wdt/weblogic-deploy/bin/compareModel.sh \ - -oracle_home $ORACLE_HOME \ - -output_dir /tmp \ - -variable_file old.properties \ - old.yaml \ - new.yaml - ``` -1. The compareModel will generate these files: - ``` - # /tmp/diffed_model.json - # /tmp/diffed_model.yaml, and - # /tmp/compare_model_stdout - ``` - -#### Changing a Domain `restartVersion` or `introspectVersion` - -As was mentioned in [Offline updates](#offline-updates), one way to tell the operator to -apply offline configuration changes to a running domain is by altering the Domain -`spec.restartVersion`. Similarly, an [online update](#online-updates) is initiated by altering -the Domain `spec.introspectVersion`. Here are some common ways to alter either of these fields: - - - You can alter `restartVersion` or `introspectVersion` interactively using `kubectl edit -n MY_NAMESPACE domain MY_DOMAINUID`. - - - If you have your domain's resource file, then you can alter this file and call `kubectl apply -f` on the file. - - - You can use the Kubernetes `get` and `patch` commands. - - Here's a sample automation script for `restartVersion` - that takes a namespace as the first parameter (default `sample-domain1-ns`) - and a domainUID as the second parameter (default `sample-domain1`): - - ```bash - #!/bin/bash - NAMESPACE=${1:-sample-domain1-ns} - DOMAINUID=${2:-sample-domain1} - currentRV=$(kubectl -n ${NAMESPACE} get domain ${DOMAINUID} -o=jsonpath='{.spec.restartVersion}') - if [ $? = 0 ]; then - # we enter here only if the previous command succeeded - - nextRV=$((currentRV + 1)) - - echo "@@ Info: Rolling domain '${DOMAINUID}' in namespace '${NAMESPACE}' from restartVersion='${currentRV}' to restartVersion='${nextRV}'." - - kubectl -n ${NAMESPACE} patch domain ${DOMAINUID} --type='json' \ - -p='[{"op": "replace", "path": "/spec/restartVersion", "value": "'${nextRV}'" }]' - fi - ``` - - Here's a similar sample script for `introspectVersion`: - - ```bash - #!/bin/bash - NAMESPACE=${1:-sample-domain1-ns} - DOMAINUID=${2:-sample-domain1} - currentIV=$(kubectl -n ${NAMESPACE} get domain ${DOMAINUID} -o=jsonpath='{.spec.introspectVersion}') - if [ $? = 0 ]; then - # we enter here only if the previous command succeeded - - nextIV=$((currentIV + 1)) - - echo "@@ Info: Rolling domain '${DOMAINUID}' in namespace '${NAMESPACE}' from introspectVersion='${currentIV}' to introspectVersion='${nextIV}'." - - kubectl -n ${NAMESPACE} patch domain ${DOMAINUID} --type='json' \ - -p='[{"op": "replace", "path": "/spec/introspectVersion", "value": "'${nextIV}'" }]' - fi - ``` - - - You can use a WebLogic Kubernetes Operator sample script that invokes - the same commands that are described in the previous bulleted item. - - See `patch-restart-version.sh` and `patch-introspect-version.sh` in - the `kubernetes/samples/scripts/create-weblogic-domain/model-in-image/utils/` - directory. - - Or, see the more advanced `introspectDomain.sh` and `rollDomain.sh` among - the [Domain lifecycle sample scripts]({{< relref "/samples/domains/lifecycle/_index.md">}}). ++++ +title = "Runtime updates" +date = 2020-03-11T16:45:16-05:00 +weight = 50 +pre = " " +description = "Updating a running Model in Image domain's images and model files." ++++ + + +{{< table_of_contents >}} + +### Overview + +If you want to make a WebLogic domain home configuration update to a running Model in Image domain, +and you want the update to survive WebLogic Server pod restarts, +then you must modify your existing model and instruct the WebLogic Kubernetes Operator to propagate the change. + +If instead you make a direct runtime WebLogic configuration update of a Model in Image domain +using the WebLogic Server Administration Console or WLST scripts, +then the update will be ephemeral. +This is because a Model in Image domain home is regenerated from the model on every pod restart. + +There are two approaches for propagating model updates to a running Model in Image domain +without first shutting down the domain: + +- _Offline updates_: [Offline updates](#offline-updates) are propagated to WebLogic pods by updating your model + and then initiating a domain roll, which generates a new domain configuration, + restarts the domain's WebLogic Server Administration Server with the updated configuration, + and then restarts other running servers. + + - _Online updates_: If model changes are configured to fully dynamic configuration MBean attributes, + then you can optionally propagate changes to WebLogic pods without a roll using an [online update](#online-updates). + If an online update request includes non-dynamic model updates that can only be achieved + using an offline update, + then the resulting behavior is controlled by the domain resource YAML + `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` attribute, + which is described in detail in [Online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). + +The operator does not support all types of WebLogic configuration changes while a domain is still running. +If a change is unsupported for an online or offline update, then propagating +the change requires entirely shutting domain the domain, +applying the change, and finally restarting the domain. Full domain restarts are described in +[Full domain restarts]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#full-domain-restarts">}}). + +**NOTE**: If you are using WebLogic Server 14.1.2.0.0 or later, see [Using demo SSL certificates in v14.1.2.0.0 or later]({{< relref "/managing-domains/model-in-image/overview#using-demo-ssl-certificates-in-v141200-or-later" >}}). + +**NOTE**: Supported and unsupported changes are described in these sections: [Supported updates](#supported-updates) and [Unsupported updates](#unsupported-updates). +_It is the administrator's responsibility to make the necessary changes to a domain resource to initiate the correct approach for an update._ + +{{% notice note %}} +Custom configuration overrides, which are WebLogic configuration overrides +specified using a domain resource YAML file `configuration.overridesConfigMap`, as described in +[Configuration overrides]({{< relref "/managing-domains/configoverrides/_index.md" >}}), +are _not_ supported in combination with Model in Image. +Model in Image will generate an error if custom overrides are specified. +This should not be a concern because model file, secret, or model image updates are simpler +and more flexible than custom configuration override updates. +Unlike configuration overrides, the syntax for a model file update exactly matches +the syntax for specifying your model file originally. +{{% /notice %}} + +### Updating an existing model + +If you have verified your proposed model updates to a running +Model in Image domain are supported by consulting +[Supported updates](#supported-updates) +and +[Unsupported updates](#unsupported-updates), +then you can use the following approaches. + +For online or offline updates: + + - Specify a new or changed WDT ConfigMap that contains model files + and use your domain resource YAML file `configuration.model.configMap` field to reference the map. + The model files in the ConfigMap will be merged with any model files in the image. + Ensure the ConfigMap is deployed to the same namespace as your domain. + + - Change, add, or delete secrets that are referenced by macros in your model files + and use your domain resource YAML file `configuration.secrets` field to reference the secrets. + Ensure the secrets are deployed to the same namespace as your domain. + +For offline updates only, there are two additional options: + + - Supply a new image with new or changed model files. + - If the files are located in the image specified in the domain resource YAML file `spec.image`, + then change this field to reference the image. + - If you are using + [auxiliary images]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) + to supply + model files in an image, then change the corresponding `serverPod.auxiliaryImages.image` field + value to reference the new image or add a new `serverPod.auxiliaryImages` mount for + the new image. + + - Change, add, or delete environment variables that are referenced by macros in your model files. + Environment variables are specified in the domain resource YAML file `spec.serverPod.env` + or `spec.serverPod.adminServer.env` attributes. + +{{% notice tip %}} +It is advisable to defer the last two modification options, or similar domain resource YAML file changes to +[fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}), +until all of your other modifications are ready. +This is because such changes automatically and immediately result in a rerun of your introspector job, +and, if the job succeeds, then a roll of the domain, +plus, an offline update, if there are any accompanying model changes. +{{% /notice %}} + +Model updates can include additions, changes, and deletions. For help generating model changes: + + - For a description of model file syntax, see the + [WebLogic Deploy Tooling](https://oracle.github.io/weblogic-deploy-tooling/) documentation + and Model in Image [Model files]({{< relref "/managing-domains/model-in-image/model-files.md" >}}) documentation. + + - For a description of helper tooling that you can use to generate model change YAML, + see [Using the WDT Discover and Compare Model Tools](#using-the-wdt-discover-domain-and-compare-model-tools). + + - If you specify multiple model files in your image, volumes (including those based on images from the [auxiliary images]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) feature), or WDT ConfigMap, + then the order in which they're loaded and merged is determined as described in + [Model file naming and loading order]({{< relref "/managing-domains/model-in-image/model-files/_index.md#model-file-naming-and-loading-order" >}}). + + - If you are performing an online update and the update includes deletes, then + see [Online update handling of deletes](#online-update-handling-of-deletes). + +After your model updates are prepared, you can instruct the operator to propagate the changed model +to a running domain by following the steps in [Offline updates](#offline-updates) +or [Online updates](#online-updates). + +### Offline updates + +Use the following steps to initiate an offline configuration update to your model: + + 1. Ensure your updates are supported by checking [Supported](#supported-updates) and [Unsupported](#unsupported-updates) updates. + 1. Modify, add, or delete your model resources as per [Updating an existing model](#updating-an-existing-model). + 1. Modify your domain resource YAML file: + 1. If you have updated your image: + - If the files are located in the image specified in the domain resource YAML file `spec.image`, + then change this field to reference the image. + - If you are using + [auxiliary images]({{< relref "/managing-domains/model-in-image/auxiliary-images.md" >}}) + to supply + model files in an image, then change the corresponding `serverPod.auxiliaryImages.image` field + value to reference the new image or add a new `serverPod.auxiliaryImages` mount for + the new image. + 1. If you are updating environment variables, change `domain.spec.serverPod.env` + or `domain.spec.adminServer.serverPod.env` accordingly. + 1. If you are specifying a WDT ConfigMap, then set `domain.spec.configuration.model.configMap` + to the name of the ConfigMap. + 1. If you are adding or deleting secrets as part of your change, then ensure + the `domain.spec.configuration.secrets` array reflects all current secrets. + 1. If you have modified your image or environment variables, + then no more domain resource YAML file changes are needed; + otherwise, change an attribute that instructs the operator to roll the domain. + For examples, see + [change the domain `spec.restartVersion`](#changing-a-domain-restartversion-or-introspectversion) + or change any of the other Domain resource YAML [fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}). + +The operator will subsequently rerun the domain's introspector job. +This job will reload all of your secrets and environment variables, +merge all of your model files, and generate a new domain home. + +If the job succeeds, then the operator will make the updated domain home available to pods +using a ConfigMap named `DOMAIN_UID-weblogic-domain-introspect-cm` and the operator will +subsequently roll (restart) each running WebLogic Server pod in the domain +so that it can load the new configuration. +A domain roll begins by restarting the domain's Administration Server and +then proceeds to restart each Managed Server in the domain. + +If the job reports a failure, see +[Debugging]({{< relref "/managing-domains/debugging.md" >}}) +for advice. + +#### Offline update sample + +For an offline update sample which adds a data source, see the +[Update 1 use case]({{< relref "/samples/domains/model-in-image/update1.md" >}}) +in the Model in Image sample. + +### Online updates + +Use the following steps to initiate an online configuration update to your model: + + 1. Ensure your updates are supported by checking [Supported](#supported-updates) and [Unsupported](#unsupported-updates) updates. + 1. Modify, add, or delete your model secrets or WDT ConfigMap + as per [Updating an existing model](#updating-an-existing-model). + 1. Modify your domain resource YAML file: + 1. **Do not** change `domain.spec.image`, `domain.spec.serverPod.env`, or any other domain resource YAML + [fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}); + this will automatically and immediately result in a rerun of your introspector job, + a roll if the job succeeds, plus an offline update if there are any accompanying model changes. + 1. If you are specifying a WDT ConfigMap, then set `domain.spec.configuration.model.configMap` + to the name of the ConfigMap. + 1. If you are adding or deleting secrets as part of your change, then ensure the + `domain.spec.configuration.secrets` array reflects all current secrets. + 1. Set `domain.spec.configuration.model.onlineUpdate.enabled` to `true` (default is `false`). + 1. Set `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` to one of + `CommitUpdateOnly` (default), and `CommitUpdateAndRoll`. + For details, see + [online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). + + 1. Optionally, tune the WDT timeouts in `domain.spec.configuration.model.onlineUpdate.wdtTimeouts`. + - This is only necessary in the rare case when an introspector job's WDT online update command timeout + results in an error in the introspector job log or operator log. + - All timeouts are specified in milliseconds and default to two or three minutes. + - For a full list of timeouts, you can call + `kubectl explain domain.spec.configuration.model.onlineUpdate.wdtTimeouts`. + 1. Change `domain.spec.introspectVersion` to a different value. For examples, see + [change the domain `spec.introspectVersion`](#changing-a-domain-restartversion-or-introspectversion). + +After you've completed these steps, the operator will subsequently run an introspector Job which +generates a new merged model, +compares the new merged model to the previously deployed merged model, +and runs WebLogic Deploy Tooling to process the differences: + + - If the introspector job WDT determines that the differences are confined to + fully dynamic WebLogic configuration MBean changes, + then the operator will send delta online updates to the running WebLogic pods. + + - If WDT detects non-dynamic WebLogic configuration MBean changes, then the operator may ignore the updates, + honor only the online updates, or initiate an offline update (roll) depending on whether you have configured + `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` to `CommitUpdateOnly` (default), or + `CommitUpdateAndRoll`. + For details, see + [online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). + + +If the introspector job reports a failure or any other failure occurs, then +see [Debugging]({{< relref "/managing-domains/debugging.md" >}}) for advice. +When recovering from a failure, please keep the following points in mind: + + - The operator cannot automatically revert changes to resources that are under + user control (just like with offline updates). For example, it is the administrator's + responsibility to revert problem changes to an image, configMap, secrets, and domain resource YAML file. + + - If there is any failure during an online update, then no WebLogic configuration changes + are made to the running domain and the introspector job retries up to the failure retry time + limit specified in `domain.spec.failureRetryLimitMinutes`. + To correct the problem, modify and reapply your model resources (ConfigMap and/or secrets), + plus, if the introspector job has stopped retrying, you must also change your domain resource + `domain.spec.introspectVersion` again. For more information, see [Domain failure retry processing]({{< relref "/managing-domains/domain-lifecycle/retry.md" >}}). + + + +Sample domain resource YAML file for an online update: + +```yaml +... +kind: Domain +metadata: + name: sample-domain1 + namespace: sample-domain1-ns +... +spec: + ... + introspectVersion: 5 + configuration: + ... + model: + domainType: "WLS" + configMap: sample-domain1-wdt-config-map + runtimeEncryptionSecret: sample-domain1-runtime-encryption-secret + onlineUpdate: + enabled: true + onNonDynamicChanges: "CommitUpdateAndRoll" + secrets: + - sample-domain1-datasource-secret + - sample-domain1-another-secret +``` + +#### Online update scenarios + +1. Successful online update that includes only dynamic WebLogic MBean changes. + * Example dynamic WebLogic MBean changes: + * Changing data source connection pool capacity, password, and targets. + * Changing application targets. + * Deleting or adding a data source. + * Deleting or adding an application. + * The MBean changes are committed in the running domain and effective immediately. + * Expected outcome after the introspector job completes: + * The domain `Completed` condition status is set to `True`. + For more information, see [Domain conditions]({{< relref "/managing-domains/accessing-the-domain/status-conditions#types-of-domain-conditions" >}}). + * The `weblogic.introspectVersion` label on all pods will be set to match the `domain.spec.introspectVersion`. + * Actions required: + * None. + +1. Successful online update that includes non-dynamic WebLogic MBean attribute changes when `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateOnly` (the default). + * Example non-dynamic WebLogic MBean change: + * Changing a data source driver parameter property (such as `username`). + * Expected outcome after the introspector job completes: + * Any dynamic WebLogic configuration changes are committed in the running domain and effective immediately. + * Non-dynamic WebLogic configuration changes will not take effect on already running WebLogic Server pods until an administrator subsequently rolls the pod. + * The domain status `Available` condition will have a `Status` of `True`. + * The domain status `ConfigChangesPendingRestart` condition will have a `Status` of `True` until an administrator subsequently rolls all WebLogic Server pods that are already running. + * Each WebLogic Server pod's `weblogic.introspectVersion` label will match `domain.spec.introspectVersion`. + * Each WebLogic Server pod that is already running will be given a `weblogic.configChangesPendingRestart=true` label until an administrator subsequently rolls the pod. + * Actions required: + * If you want the non-dynamic changes to take effect, then restart the pod(s) with the `weblogic.configChangesPendingRestart=true` label (such as by initiating a domain roll). + * See [Online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). + +1. Successful online update that includes non-dynamic WebLogic MBean attribute changes when `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateAndRoll`. + * Expected outcome after the introspector job completes: + * Any dynamic WebLogic configuration changes are committed in the running domain and effective immediately. + * The operator will initiate a domain roll. + * Non-dynamic WebLogic configuration changes will take effect on each pod when the pod is rolled. + * Each WebLogic Server pod's `weblogic.introspectVersion` label will match `domain.spec.introspectVersion` after it is rolled. + * The domain status `Available` condition will have a `Status` of `True` after the roll completes. + * Actions required: + * None. All changes will complete after the operator initiated domain roll completes. + * See [Online update handling of non-dynamic WebLogic configuration changes](#online-update-handling-of-non-dynamic-weblogic-configuration-changes). + +1. Changing any of the domain resource [fields that cause servers to be restarted]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#fields-that-cause-servers-to-be-restarted" >}}) in addition to `domain.spec.introspectVersion`, `spec.configuration.secrets`, `spec.configuration.model.onlineUpdate`, or `spec.configuration.model.configMap`. + * Expected outcome after the introspector job completes: + * No online update was attempted by the introspector job. + * All model changes are treated the same as offline updates (which may result in restarts/roll after job success). + * Actions required: + * None. + +1. Changing any model attribute that is [unsupported](#unsupported-updates). + * Expected outcome: + * The expected behavior is often undefined, but in some cases there will be helpful error in the introspector job, events, and/or domain status, and the job will periodically retry until the error is corrected or its maximum error count exceeded. + * Actions required: + * Use offline updates if they are supported, or, if not, shutdown the entire domain and restart it. + * See [Debugging]({{< relref "/managing-domains/debugging.md" >}}). + +1. Errors in the model; for example, a syntax error. + * Expected outcome after the introspector job completes: + * Error in the introspector job's pod log, domain events, and domain status. + * The domain status `Failed` condition will have a `Status` of `True`. + * Periodic job retries until the error is corrected or until a maximum error count is exceeded. + * Actions required: + * Correct the model. + * If retries have halted, then alter the `spec.introspectVersion`. + +1. Other errors while updating the domain. + * Expected outcome: + * Error in the introspector job, domain events, and/or domain status. + * The domain status `Failed` condition will have a `Status` of `True`. + * If there's a failed introspector job, the job will retry periodically until the error is corrected or until it exceeds its maximum error count. Other types of errors will also usually incur periodic retries. + * Actions required: + * See [Debugging]({{< relref "/managing-domains/debugging.md" >}}). + * Make corrections to the domain resource and/or model. + * If retries have halted, then alter the `spec.introspectVersion`. + + +#### Online update status and labels + +During an online update, the operator will rerun the introspector job, which +in turn attempts online WebLogic configuration changes to the running domain. +You can monitor an update's status using its domain resource's status conditions +and its WebLogic Server pod labels. + +For example, for the domain status +you can check the domain resource `domain.status` stanza +using `kubectl -n MY_NAMESPACE get domain MY_DOMAINUID -o yaml`, +and for the WebLogic pod labels you can use +`kubectl -n MY_NAMESPACE get pods --show-labels` plus +optionally add `--watch` to watch the pods as they change over time. + +The `ConfigChangesPendingRestart` condition in `domain.status` contains information about the progress +of the online update. See [ConfigChangesPendingRestart condition]({{< relref "/managing-domains/accessing-the-domain/status-conditions#configchangespendingrestart" >}}) for details. + +Here are some of the expected WebLogic pod labels after an online update success: + + 1. Each WebLogic Server pod's `weblogic.introspectVersion` label value + will eventually match the `domain.spec.introspectVersion` value that you defined. + * If the domain resource attribute + `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateOnly` (the default), + then the introspect version label on all pods is immediately updated + after the introspection job successfully completes. + * If the domain resource attribute + `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateAndRoll` + and there are no non-dynamic configuration changes to the model, + then the introspect version label on all pods is immediately updated + after the introspection job successfully completes. + * If the domain resource attribute + `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` is `CommitUpdateAndRoll` + and there are non-dynamic clabel onfiguration changes to the model, + then the introspect version label on each pod is updated after the pod is rolled. + + 1. There will be a `weblogic.configChangesPendingRestart=true` label on each + WebLogic Server pod until the pod is restarted (rolled) by an administrator + if all of the following are true: + * The domain resource `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` + attribute is `CommitUpdateOnly` (the default). + * Non-dynamic WebLogic configuration changes were included in a + successful online model update. + +#### Online update handling of non-dynamic WebLogic configuration changes + +The domain resource YAML `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges` attribute +controls behavior when non-dynamic WebLogic configuration changes are detected during an online update introspector job. +Non-dynamic changes are changes that require a domain restart to take effect. +Valid values are `CommitUpdateOnly` (default), or `CommitUpdateAndRoll`: + +* If set to `CommitUpdateOnly` (the default) and any non-dynamic changes are detected, + then all changes will be committed, + dynamic changes will take effect immediately, + the domain will not automatically restart (roll), + and any non-dynamic changes will become effective on a pod only when + the pod is later restarted. + +* If set to `CommitUpdateAndRoll` and any non-dynamic changes are detected, + then all changes will be committed, dynamic changes will take effect immediately, + the domain will automatically restart (roll), + and non-dynamic changes will take effect on each pod after the pod restarts. + +{{% notice note %}} + When updating a domain with non-dynamic MBean changes with + `domain.spec.configuration.model.onlineUpdate.onNonDynamicChanges=CommitUpdateOnly` (the default), + the non-dynamic changes are not effective on a WebLogic pod until the pod is restarted. + However, if you scale up a cluster or otherwise start any new servers in the domain, + then the new servers will start with the new non-dynamic changes + and the domain will then be running in an inconsistent state until its older servers are restarted. +{{% /notice %}} + +#### Online update handling of deletes + +The primary use case for online updates is to make small additions, + deletions of single resources or MBeans that have no dependencies, + or changes to non-dynamic MBean attributes. + +Deletion can be problematic for online updates in two cases: +- Deleting multiple resources that have cross dependencies. +- Deleting the parent type section in an MBean hierarchy. + +In general, complex deletion should be handled by offline updates + to avoid these problems. + +**NOTE**: Implicitly removing a model's parent type + section may sometimes work depending + on the type of the section. For example, if you have an application + in the model under `appDeployments:` in a `model.configMap` and you + subsequently update the ConfigMap using an online update so that it + no longer includes the `appDeployment` section, then the online update + will delete the application from the domain. + +##### MBean type section deletion + +For an example of an MBean deletion, consider a WDT ConfigMap that starts with: + +```yaml + resources: + SelfTuning: + WorkManager: + wm1: + Target: 'cluster-1' + wm2: + Target: 'cluster-1' + JDBCSystemResource: + ... +``` + +If you want to online update to a new model without `work-managers`, + then change the ConfigMap to the following: + +```yaml + resources: + SelfTuning: + WorkManager: + JDBCSystemResource: + ... +``` + +Or, supply an additional ConfigMap: + +```yaml + resources: + SelfTuning: + WorkManager: + '!wm1': + '!wm2': +``` + +The online update will fail if you try replace the ConfigMap + with the `SelfTuning` section omitted: + +```yaml + resources: + JDBCSystemResource: + ... +``` + +This fails because it implicitly removes + the MBean types `SelfTuning` and `WorkManager`. + +##### Deleting cross-referenced MBeans + +For an example of an unsupported online update delete of MBeans + with cross references, consider the case of a Work Manager + configured with constraints where you want to delete the entire Work Manager: + +```yaml + resources: + SelfTuning: + WorkManager: + newWM: + Target: 'cluster-1' + MinThreadsConstraint: 'SampleMinThreads' + MaxThreadsConstraint: 'SampleMaxThreads' + MinThreadsConstraint: + SampleMinThreads: + Count: 1 + MaxThreadsConstraint: + SampleMaxThreads: + Count: 10 +``` + +If you try to specify the updated model in the ConfigMap as: + +```yaml + resources: + SelfTuning: + WorkManager: + MinThreadsConstraint: + MaxThreadsConstraint: + +``` + +Then, the operator will try use this delta to online update the domain: + +```yaml + resources: + SelfTuning: + MaxThreadsConstraint: + '!SampleMaxThreads': + WorkManager: + '!newWM': + MinThreadsConstraint: + '!SampleMinThreads': +``` + +This can fail because an online update might not delete all the referenced `Constraints` first + before deleting the `WorkManager`. + +To work around problems with online updates to objects with cross dependencies, you can + use a series of online updates to make the change in stages. For example, continuing the previous Work Manager + example, first perform an online update to omit the Work Manager but not the constraints: + +```yaml + resources: + SelfTuning: + WorkManager: + MinThreadsConstraint: + SampleMinThreads: + Count: 1 + MaxThreadsConstraint: + SampleMaxThreads: + Count: 10 +``` + +After that update completes, then perform another online update: + +```yaml + resources: + SelfTuning: + WorkManager: + MinThreadsConstraint: + MaxThreadsConstraint: +``` + +#### Online update sample + +For an online update sample which alters a data source and Work Manager, see the +[Update 4 use case]({{< relref "/samples/domains/model-in-image/update4.md" >}}) +in the Model in Image sample. + +### Appendices + +Review the following appendices for additional, important information. + +#### Supported updates + +The following updates are *supported* for offline or online updates, +except when they reference an area that is specifically +documented as [unsupported](#unsupported-updates): + + - You can add a new WebLogic cluster or standalone server. + + - You can increase the size of a dynamic WebLogic cluster. + + - You can add new MBeans or resources by specifying their corresponding model YAML file snippet + along with their parent bean hierarchy. For example, you can add a data source. + + - You can change or add MBean attributes by specifying a YAML file snippet + along with its parent bean hierarchy that references an existing MBean and the attribute. + For example, to add or alter the maximum capacity of a data source named `mynewdatasource`: + + ```yaml + resources: + JDBCSystemResource: + mynewdatasource: + JdbcResource: + JDBCConnectionPoolParams: + MaxCapacity: 5 + ``` + + For more information, see [Using Multiple Models](https://oracle.github.io/weblogic-deploy-tooling/concepts/model/#using-multiple-models) in the WebLogic Deploy Tooling documentation. + + - You can change or add secrets that your model macros reference + (macros that use the `@@SECRET:secretname:secretkey@@` syntax). + For example, you can change a database password secret. + + - For offline updates only, you can change or add environment variables + that your model macros reference + (macros that use the `@@ENV:myenvvar@@` syntax). + + - You can remove an MBean, application deployment, or resource by omitting any + reference to it in your image model files and WDT config map. + You can also remove a named MBean, application deployment, or resource + by specifying an additional model file with an exclamation point (`!`) + just before its name plus ensuring the new model file is loaded _after_ + the original model file that contains the original named configuration. + For example, if you have a data source named `mynewdatasource` defined + in your model, then it can be removed by specifying a small model file that + loads after the model file that defines the data source, where the + small model file looks like this: + + ```yaml + resources: + JDBCSystemResource: + !mynewdatasource: + ``` + There are [some exceptions for online updates](#online-update-handling-of-deletes). + + For more information, see + [Declaring Named MBeans to Delete](https://oracle.github.io/weblogic-deploy-tooling/concepts/model/#declaring-named-mbeans-to-delete) + in the WebLogic Deploying Tooling documentation. + +#### Unsupported updates + +{{% notice warning %}} +It is important to avoid applying unsupported model updates to a running domain. An attempt to use an unsupported update may not always result in a clear error message, and the expected behavior may be undefined. If you need to make an unsupported update and no workaround is documented, then shut down your domain entirely before making the change. See [Full domain restarts]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#full-domain-restarts">}}). +{{% /notice %}} + +The following summarizes the types of runtime update configuration that are _not_ supported in Model in Image unless a workaround or alternative is documented: + + - Altering cluster size: + - You have a limited ability to change an existing WebLogic cluster's membership. + - Specifically, do _not_ apply runtime updates for: + - Adding WebLogic Servers to a configured cluster. As an alternative, consider using dynamic clusters instead of configured clusters. + - Removing WebLogic Servers from a configured cluster. As an alternative, you can lower your cluster's domain resource YAML `replicas` attribute. + - Decreasing the size of a dynamic cluster. As an alternative, you can lower your cluster's domain resource YAML `replicas` attribute. + - You cannot change, add, or remove network listen address, port, protocol, and enabled configuration for existing clusters or servers at runtime. + - Specifically, do not apply runtime updates for: + - A Default, SSL, Admin channel `Enabled`, listen address, or port. + - A Network Access Point (custom channel) `Enabled`, listen address, protocol, or port. + - Note that it is permitted to override network access point `public` or `external` addresses and ports. + External access to JMX (MBean) or online WLST requires that the network access point internal port + and external port match (external T3 or HTTP tunneling access to JMS, RMI, or EJBs don't require port matching). + + {{% notice warning %}} + Due to security considerations, we strongly recommend that T3 or any RMI protocol should not be exposed outside the cluster. + {{% /notice %}} + + + - Node Manager related configuration. + - Log related settings. This applies to changing, adding, or removing server and domain log related settings in an MBean at runtime + when the domain resource is configured to override the same MBeans using the `spec.logHome`, + `spec.logHomeEnabled`, or `spec.httpAccessLogInLogHome` attributes. + - Changing the domain name. You cannot change the domain name at runtime. + - Deleting an MBean attribute: + - There is no way to directly delete an attribute from an MBean that's already been specified by a model file. + - The workaround is to do this using two model files: + - Add a model file that deletes the named bean/resource that is a parent to + the attribute you want to delete using the `!` syntax as described in [Supported Updates](#supported-updates). + - Add another model file that will be loaded after the first one, + which fully defines the named bean/resource but without the attribute you want to delete. + - Changing any existing MBean name: + - There is no way to directly change the MBean name of an attribute. + - Instead, you can remove a named MBean using the `!` syntax as described in [Supported Updates](#supported-updates). + - Then, you add a new one as a replacement. + - Embedded LDAP entries: + - Embedded LDAP security entries for [users, groups, roles](https://oracle.github.io/weblogic-deploy-tooling/samples/usersgroups-model/), + and [credential mappings](https://oracle.github.io/weblogic-deploy-tooling/samples/pwcredentialmap-model/). For example, you cannot add a user to the default security realm. + - Online update attempts in this area will fail during the introspector job, and offline update attempts may result in inconsistent security checks during the offline update's rolling cycle. + - If you need to make these kinds of updates, then shut down your domain entirely before making the change, + or switch to an [external security provider](https://oracle.github.io/weblogic-deploy-tooling/samples/securityproviders-model/). + - Any Model YAML `topology:` stanza changes: + - For example, `ConsoleEnabled`, `RootDirectory`, `AdminServerName`, and such. + - For a complete list, run `/u01/wdt/weblogic-deploy/bin/modelHelp.sh -oracle_home $ORACLE_HOME topology` + (this assumes you have installed WDT in the `/u01/wdt/weblogic-deploy` directory). + - Dependency deletion in combination with online updates. + - Deleting Model entries by type: + - For example, you cannot delete an entire `SelfTuning` type stanza + by omitting the stanza in an online update or by specifying an additional model with `!SelfTuning` + in either an offline or an online update. + - Instead, you can delete the specific MBean by omitting the MBean itself while leaving its `SelfTuning` + parent in place or by specifying an additional model using the `!` syntax in combination + with the name of the specific MBean. + - For details, see [Online update handling of deletes](#online-update-handling-of-deletes). + - Deleting multiple resources that have cross-references in combination with online updates: + - For example, concurrently deleting a persistent store and a data source referenced by the persistent store. + - For this type of failure, the introspection job will fail and log an error describing + the failed reference, and the job will automatically retry up to its maximum retries. + - For details, see [Online update handling of deletes](#online-update-handling-of-deletes). + - Security related changes in combination with online updates: + - Such changes included security changes in `domainInfo.Admin*`, + `domainInfo.RCUDbinfo.*`, `topology.Security.*`, and `topology.SecurityConfiguration.*`. + - Any online update changes in these sections will result in a failure. + +#### Using the WDT Discover Domain and Compare Model Tools + +Optionally, you can use the WDT Discover Domain and Compare Domain Tools to help generate your model file updates. +The WebLogic Deploy Tooling +[Discover Domain Tool](https://oracle.github.io/weblogic-deploy-tooling/userguide/tools/discover/) +generates model files from an existing domain home, +and its [Compare Model Tool](https://oracle.github.io/weblogic-deploy-tooling/userguide/tools/compare/) +compares two domain models and generates the YAML file for updating the first domain to the second domain. + +For example, assuming you've installed WDT in `/u01/wdt/weblogic-deploy` and assuming your domain type is `WLS`: + +1. Run discover for your existing domain home. + + ```shell + $ /u01/wdt/weblogic-deploy/bin/discoverDomain.sh \ + -oracle_home $ORACLE_HOME \ + -domain_home $DOMAIN_HOME \ + -domain_type WLS \ + -archive_file old.zip \ + -model_file old.yaml \ + -variable_file old.properties + ``` + +1. Now make some WebLogic config changes using the console or WLST. + +1. Run discover for your changed domain home. + + ```shell + $ /u01/wdt/weblogic-deploy/bin/discoverDomain.sh \ + -oracle_home $ORACLE_HOME \ + -domain_home $DOMAIN_HOME \ + -domain_type WLS \ + -archive_file new.zip \ + -model_file new.yaml \ + -variable_file new.properties + ``` +1. Compare your old and new YAML using `diff`. + + ```shell + $ diff new.yaml old.yaml + ``` +1. Compare your old and new YAML using compareDomain to generate the YAML update file you can use for transforming the old to new. + ``` + $ /u01/wdt/weblogic-deploy/bin/compareModel.sh \ + -oracle_home $ORACLE_HOME \ + -output_dir /tmp \ + -variable_file old.properties \ + old.yaml \ + new.yaml + ``` +1. The compareModel will generate these files: + ``` + # /tmp/diffed_model.json + # /tmp/diffed_model.yaml, and + # /tmp/compare_model_stdout + ``` + +#### Changing a Domain `restartVersion` or `introspectVersion` + +As was mentioned in [Offline updates](#offline-updates), one way to tell the operator to +apply offline configuration changes to a running domain is by altering the Domain +`spec.restartVersion`. Similarly, an [online update](#online-updates) is initiated by altering +the Domain `spec.introspectVersion`. Here are some common ways to alter either of these fields: + + - You can alter `restartVersion` or `introspectVersion` interactively using `kubectl edit -n MY_NAMESPACE domain MY_DOMAINUID`. + + - If you have your domain's resource file, then you can alter this file and call `kubectl apply -f` on the file. + + - You can use the Kubernetes `get` and `patch` commands. + + Here's a sample automation script for `restartVersion` + that takes a namespace as the first parameter (default `sample-domain1-ns`) + and a domainUID as the second parameter (default `sample-domain1`): + + ```bash + #!/bin/bash + NAMESPACE=${1:-sample-domain1-ns} + DOMAINUID=${2:-sample-domain1} + currentRV=$(kubectl -n ${NAMESPACE} get domain ${DOMAINUID} -o=jsonpath='{.spec.restartVersion}') + if [ $? = 0 ]; then + # we enter here only if the previous command succeeded + + nextRV=$((currentRV + 1)) + + echo "@@ Info: Rolling domain '${DOMAINUID}' in namespace '${NAMESPACE}' from restartVersion='${currentRV}' to restartVersion='${nextRV}'." + + kubectl -n ${NAMESPACE} patch domain ${DOMAINUID} --type='json' \ + -p='[{"op": "replace", "path": "/spec/restartVersion", "value": "'${nextRV}'" }]' + fi + ``` + + Here's a similar sample script for `introspectVersion`: + + ```bash + #!/bin/bash + NAMESPACE=${1:-sample-domain1-ns} + DOMAINUID=${2:-sample-domain1} + currentIV=$(kubectl -n ${NAMESPACE} get domain ${DOMAINUID} -o=jsonpath='{.spec.introspectVersion}') + if [ $? = 0 ]; then + # we enter here only if the previous command succeeded + + nextIV=$((currentIV + 1)) + + echo "@@ Info: Rolling domain '${DOMAINUID}' in namespace '${NAMESPACE}' from introspectVersion='${currentIV}' to introspectVersion='${nextIV}'." + + kubectl -n ${NAMESPACE} patch domain ${DOMAINUID} --type='json' \ + -p='[{"op": "replace", "path": "/spec/introspectVersion", "value": "'${nextIV}'" }]' + fi + ``` + + - You can use a WebLogic Kubernetes Operator sample script that invokes + the same commands that are described in the previous bulleted item. + - See `patch-restart-version.sh` and `patch-introspect-version.sh` in + the `kubernetes/samples/scripts/create-weblogic-domain/model-in-image/utils/` + directory. + - Or, see the more advanced `introspectDomain.sh` and `rollDomain.sh` among + the [Domain lifecycle sample scripts]({{< relref "/samples/domains/lifecycle/_index.md">}}). From a3c33fba9f3a472926a57d800f691a50c1cd600f Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 10 Jan 2025 20:58:09 +0000 Subject: [PATCH 263/356] Merge branch 'support-non-empty-rg' into 'main' On branch support-non-empty-rg Add proviso that multiple deployments of this... See merge request weblogic-cloud/weblogic-kubernetes-operator!4901 (cherry picked from commit 3137bf73fbb31880a54ffa74018771d217a8bfee) 541b2c91 remove the "empty resource group" requirement. 7261b56a On branch support-non-empty-rg Add proviso that multiple deployments of this... --- documentation/site/content/managing-domains/aks/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/site/content/managing-domains/aks/_index.md b/documentation/site/content/managing-domains/aks/_index.md index f8684074730..05bb6c0b21f 100644 --- a/documentation/site/content/managing-domains/aks/_index.md +++ b/documentation/site/content/managing-domains/aks/_index.md @@ -33,7 +33,7 @@ Use the **Basics** blade to provide the basic configuration details for deployin | Field | Description | |-------|-------------| | Subscription | Select a subscription to use for the charges accrued by this offer. You must have a valid active subscription associated with the Azure account that is currently logged in. If you don’t have it already, follow the steps described in [Associate or add an Azure subscription to your Azure Active Directory tenant](https://docs.microsoft.com/azure/active-directory/fundamentals/active-directory-how-subscriptions-associated-directory).| -| Resource group | A resource group is a container that holds related resources for an Azure solution. The resource group includes those resources that you want to manage as a group. You decide which resources belong in a resource group based on what makes the most sense for your organization. If you have an existing resource group into which you want to deploy this solution, you can enter its name here; however, the resource group must have no pre-existing resources in it. Alternatively, you can click the **Create new**, and enter the name so that Azure creates a new resource group before provisioning the resources. For more information about resource groups, see the [Azure documentation](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-overview#resource-groups). | +| Resource group | A resource group is a container that holds related resources for an Azure solution. The resource group includes those resources that you want to manage as a group. You decide which resources belong in a resource group based on what makes the most sense for your organization. If you have an existing resource group into which you want to deploy this solution, you can enter its name here. **Note** deploying this solution multiple times into the same resource group is not supported. Alternatively, you can click the **Create new**, and enter the name so that Azure creates a new resource group before provisioning the resources. For more information about resource groups, see the [Azure documentation](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-overview#resource-groups). | #### Instance details From 34c90bbe4dba89ba80171052a839cf30f6c86a54 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 16 Jan 2025 16:26:58 -0500 Subject: [PATCH 264/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index b2b286ad126..087b56aaae3 100644 --- a/pom.xml +++ b/pom.xml @@ -707,14 +707,14 @@ 0.9.6 3.6.0 1.0.0 - 3.27.0 + 3.27.2 2.18.0 4.2.2 19.0.1 3.0.1u2 2.1.0 4.12.0 - 3.9.1 + 3.10.2 1.79 5.11.4 5.7.1 @@ -732,10 +732,10 @@ 2.11.0 11.1.1 2.0.16 - 1.5.14 + 1.5.16 4.29.2 2.5.1 - 9.47 + 10.0.1 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From b097aa134185d8fbc4aca9404f2791d47ee1b537 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 27 Jan 2025 13:35:29 +0000 Subject: [PATCH 265/356] Clean up terraform output to post only errors --- .../oke/terraform/okemodule/oke.create.sh | 22 +++++++++++-------- .../oke/terraform/okemodule/oke.delete.sh | 13 +++++------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh index f5689911eff..73219fea973 100755 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.create.sh @@ -68,8 +68,10 @@ deleteOlderVersionTerraformOCIProvider() { createCluster () { cd ${terraformVarDir} echo "terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars" - terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars - terraform plan -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars + terraform init -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars > /dev/null + echo "terraform plan -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform plan -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars > /dev/null + echo "terraform apply -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars" terraform apply -auto-approve -var-file=${terraformVarDir}/${clusterTFVarsFile}.tfvars } @@ -123,7 +125,7 @@ checkKubernetesCliConnection() { if [[ $retry_count -eq $max_retries ]]; then echo "Failed to connect to Kubernetes cluster after $max_retries attempts." cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null createCluster fi @@ -137,7 +139,7 @@ checkKubernetesCliConnection() { myline_output=$(${KUBERNETES_CLI:-kubectl} get nodes -o wide 2>&1) if echo "$myline_output" | grep -q "Unable to connect to the server: net/http: TLS handshake timeout"; then cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null exit 1 fi fi @@ -147,7 +149,7 @@ checkKubernetesCliConnection() { echo '- could not talk to OKE cluster, aborting' cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null exit 1 fi @@ -165,12 +167,12 @@ checkClusterRunning() { if [ ! -f "$kubeconfig_file" ]; then echo "Kubeconfig file does not exist." cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null createCluster else echo "Kubeconfig file exists but is empty." cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null createCluster fi fi @@ -203,14 +205,14 @@ checkClusterRunning() { else echo '- could not talk to OKE cluster, aborting' cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null exit 1 fi if [ $count -gt $max ]; then echo "[ERROR] Unable to start the nodes in the OKE cluster after 200s" cd "${terraformVarDir}" - terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" + terraform destroy -auto-approve -var-file="${terraformVarDir}/${clusterTFVarsFile}.tfvars" > /dev/null exit 1 fi } @@ -262,6 +264,8 @@ deleteOlderVersionTerraformOCIProvider chmod 600 ${ocipk_path} sudo yum reinstall ca-certificates -y sudo iptables -A OUTPUT -p tcp --dport 6443 -j ACCEPT +export TF_LOG=ERROR + # run terraform init,plan,apply to create OKE cluster based on the provided tfvar file ${clusterTFVarsFile).tfvar createCluster #check status of OKE cluster nodes, destroy if can not access them diff --git a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh index 9648d87b6a8..3e99e035b26 100644 --- a/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh +++ b/integration-tests/src/test/resources/oke/terraform/okemodule/oke.delete.sh @@ -58,9 +58,9 @@ cleanupLB() { deleteOKE() { cd ${terraform_script_dir} - terraform init -var-file=${terraform_script_dir}/${clusterName}.tfvars - terraform plan -var-file=${terraform_script_dir}/${clusterName}.tfvars - terraform destroy -auto-approve -var-file=${terraform_script_dir}/${clusterName}.tfvars + terraform init -var-file=${terraform_script_dir}/${clusterName}.tfvars > /dev/null + terraform plan -var-file=${terraform_script_dir}/${clusterName}.tfvars > /dev/null + terraform destroy -auto-approve -var-file=${terraform_script_dir}/${clusterName}.tfvars > /dev/null } @@ -72,11 +72,8 @@ clusterName=$(prop 'okeclustername') compartment_ocid=$(prop 'compartment.ocid') vcn_cidr_prefix=$(prop 'vcn.cidr.prefix') export KUBECONFIG=${terraform_script_dir}/${clusterName}_kubeconfig +export TF_LOG=ERROR export PATH=${terraform_script_dir}/terraforminstall:$PATH echo 'Deleting cluster' -#check and cleanup any left over running Load Balancers -out=$(cleanupLB Subnet01 && :) -echo $out -out=$(cleanupLB Subnet02 && :) -echo $out + deleteOKE || true From 6d616ce7a548769fc35be6085e47f61585ffb821 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Tue, 21 Jan 2025 19:36:38 +0000 Subject: [PATCH 266/356] Merge branch 'rm/fluent-bit-blog' into 'main' Add x-ref to blog See merge request weblogic-cloud/weblogic-kubernetes-operator!4902 (cherry picked from commit 24c52bbdc32e9c15bcc6ec18fa3692c024bff4c6) c68f2427 Add x-ref to blog 4958c435 update text according to Johnny --- .../content/samples/elastic-stack/weblogic-domain/_index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/site/content/samples/elastic-stack/weblogic-domain/_index.md b/documentation/site/content/samples/elastic-stack/weblogic-domain/_index.md index a3b12f00c2d..d464453d18c 100644 --- a/documentation/site/content/samples/elastic-stack/weblogic-domain/_index.md +++ b/documentation/site/content/samples/elastic-stack/weblogic-domain/_index.md @@ -14,6 +14,8 @@ Here's the general mechanism for how this works: * `fluentd` tails the domain logs files and exports them to Elasticsearch. * A ConfigMap contains the filter and format rules for exporting log records. +For information on how to use Fluent Bit as a DaemonSet to scrape WLS logs, see the blog, [WLS For OKE, with Fluent bit and FSS](https://blogs.oracle.com/weblogicserver/post/wls-for-oke-with-fluent-bit-and-fss). + #### Sample code The samples in this document assume that an existing domain is being edited. However, you can make all the changes to the domain YAML file before the domain is created. From df58d7b597f7dfc0bdea6a7e97d98b9b2900eab1 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 12 Feb 2025 09:27:47 -0500 Subject: [PATCH 267/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 087b56aaae3..ec08cab7db6 100644 --- a/pom.xml +++ b/pom.xml @@ -688,7 +688,7 @@ 3.8.1 3.6.0 3.5.0 - 10.21.0 + 10.21.2 1.0 3.6.0 3.2.7 @@ -698,7 +698,7 @@ 2.0.1 1.0.39 1.9.0 - 1.5.4 + 1.5.5 1.4.0 1.17.1 1.7.3 @@ -715,13 +715,13 @@ 2.1.0 4.12.0 3.10.2 - 1.79 + 1.80 5.11.4 5.7.1 1.7.0 1.3.2 UTF-8 - 3.1.9 + 3.1.10 1.1.7 4.0.2 6.1.0 @@ -733,7 +733,7 @@ 11.1.1 2.0.16 1.5.16 - 4.29.2 + 4.29.3 2.5.1 10.0.1 ${project.basedir}/src-generated-swagger From 7a4db2bb32a4437a0680db3c13c9cc6acb8a5e02 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 28 Jan 2025 17:53:24 -0500 Subject: [PATCH 268/356] Check for operatorOnly=true prior to searching for existing webhook deployments --- .../charts/weblogic-operator/templates/_operator-dep.tpl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl index 4c23c79d395..99486fba9b2 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorDeployment" }} @@ -212,8 +212,9 @@ spec: --- {{ $chartVersion := .Chart.Version }} {{ $releaseNamespace := .Release.Namespace }} + {{- if not .operatorOnly }} {{ $webhookExists := include "utils.verifyExistingWebhookDeployment" (list $chartVersion $releaseNamespace) | trim }} - {{- if and (ne $webhookExists "true") (not .operatorOnly) }} + {{- if ne $webhookExists "true" }} # webhook does not exist or chart version is newer, create a new webhook apiVersion: "v1" kind: "ConfigMap" @@ -424,4 +425,5 @@ spec: optional: true {{- end }} {{- end }} + {{- end }} {{- end }} From 37b8b9f0c83389d18f753e780423da61e82670e8 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sat, 8 Feb 2025 11:05:32 -0500 Subject: [PATCH 269/356] Use runAsUser if explicitly set --- .../charts/weblogic-operator/templates/_operator-dep.tpl | 4 ++-- kubernetes/charts/weblogic-operator/values.yaml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl index 99486fba9b2..32363131213 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-dep.tpl @@ -117,7 +117,7 @@ spec: memory: {{ .memoryLimits }} {{- end }} securityContext: - {{- if (ne ( .kubernetesPlatform | default "Generic" ) "OpenShift") }} + {{- if or (hasKey . "runAsUser") (ne ( .kubernetesPlatform | default "Generic" ) "OpenShift") }} runAsUser: {{ .runAsUser | default 1000 }} {{- end }} runAsNonRoot: true @@ -340,7 +340,7 @@ spec: memory: {{ .memoryLimits }} {{- end }} securityContext: - {{- if (ne ( .kubernetesPlatform | default "Generic" ) "OpenShift") }} + {{- if or (hasKey . "runAsUser") (ne ( .kubernetesPlatform | default "Generic" ) "OpenShift") }} runAsUser: {{ .runAsUser | default 1000 }} {{- end }} runAsNonRoot: true diff --git a/kubernetes/charts/weblogic-operator/values.yaml b/kubernetes/charts/weblogic-operator/values.yaml index a3fc098b676..4ea032a1707 100644 --- a/kubernetes/charts/weblogic-operator/values.yaml +++ b/kubernetes/charts/weblogic-operator/values.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, Oracle and/or its affiliates. +# Copyright (c) 2018, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # serviceAccount specifies the name of the ServiceAccount in the operator's namespace that the @@ -117,7 +117,7 @@ elasticSearchPort: 9200 # This parameter is ignored if 'elkIntegrationEnabled' is false. elasticSearchProtocol: http -# createlogStashConfigMap specifies whether a ConfigMap named +# createLogStashConfigMap specifies whether a ConfigMap named # weblogic-operator-logstash-cm should be created during helm install. # The ConfigMap contains the Logstash pipeline configuration for the Logstash # container running in the operator pod. @@ -263,14 +263,14 @@ clusterSizePaddingValidationEnabled: true # tokenReviewAuthentication, if set to true, specifies whether the operator's REST API should use # 1. Kubernetes token review API for authenticating users, and # 2. Kubernetes subject access review API for authorizing a user's operation (get, list, -# patch, etc) on a resource. +# patch, etc.) on a resource. # 3. Update the Domain resource using the operator's privileges. # This parameter, if set to false, will use the caller's bearer token for any update # to the Domain resource so that it is done using the caller's privileges. # The default value is false. #tokenReviewAuthentication: false -# runAsuser specifies the UID to run the operator and conversion webhook container processes. +# runAsUser specifies the UID to run the operator and conversion webhook container processes. # If not specified, it defaults to the user specified in the operator's container image. #runAsUser: 1000 From b08665c063860f349976ec3c4909717910d34bb9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sat, 15 Feb 2025 07:42:40 -0500 Subject: [PATCH 270/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index ec08cab7db6..4f1fbdc44b5 100644 --- a/pom.xml +++ b/pom.xml @@ -707,12 +707,12 @@ 0.9.6 3.6.0 1.0.0 - 3.27.2 + 3.27.3 2.18.0 4.2.2 19.0.1 3.0.1u2 - 2.1.0 + 2.1.10 4.12.0 3.10.2 1.80 @@ -729,12 +729,12 @@ 2.18.2 2.18.2 2.3 - 2.11.0 - 11.1.1 + 2.12.1 + 12.0.2 2.0.16 1.5.16 4.29.3 - 2.5.1 + 2.5.2 10.0.1 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java From b8ca5119d34eb94d8c18408d782337624d637b49 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sat, 15 Feb 2025 08:04:57 -0500 Subject: [PATCH 271/356] Prepare for WKO 4.2.14 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 0a8607d63b7..d01a6f1e8dd 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 598914507f0..9b6448bea1f 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index ff7518ad839..6556e4ea652 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 60be74e3f4c..347400eee03 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.14-SNAPSHOT + 4.2.14 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 3d77bd3b4aa..1c916c50a31 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 452aac0e9cf..cf5ef7f0721 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.14-SNAPSHOT + 4.2.14 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 2420019a580..974fd02eeb6 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 4f1fbdc44b5..3800ff5729a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 08d868751f0..abe839569e8 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14-SNAPSHOT + 4.2.14 operator-swagger From 0a586243fabe7039585d55eef0e23f2901adabd6 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sat, 15 Feb 2025 08:32:49 -0500 Subject: [PATCH 272/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index d01a6f1e8dd..e50f26da7cf 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 9b6448bea1f..41b28319c8e 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 6556e4ea652..0c735f31e3e 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 347400eee03..2198ed6a338 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.14 + 4.2.15-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 1c916c50a31..0fe9f501c8c 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index cf5ef7f0721..7cbc9392498 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.14 + 4.2.15-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 974fd02eeb6..591f224ad70 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 3800ff5729a..4a85faa5f0b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index abe839569e8..4238c2c302f 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.14 + 4.2.15-SNAPSHOT operator-swagger From 39babb2c6bf967427c77c33aafed35420fc88335 Mon Sep 17 00:00:00 2001 From: jshum Date: Thu, 20 Feb 2025 08:40:39 -0600 Subject: [PATCH 273/356] fix aux image init container not honoring podSecurityContext ported from main. --- .../operator/helpers/PodHelper.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java index 327d32b4ca5..5bffe971766 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java @@ -169,6 +169,17 @@ private static String getServerName(@Nonnull Map labels) { return labels.get(SERVERNAME_LABEL); } + private static V1SecurityContext getEffectiveSecurityContext(V1PodSecurityContext ctx) { + return new V1SecurityContext() + .runAsUser(ctx.getRunAsUser()) + .runAsGroup(ctx.getRunAsGroup()) + .runAsNonRoot(ctx.getRunAsNonRoot()) + .seccompProfile(ctx.getSeccompProfile()) + .seLinuxOptions(ctx.getSeLinuxOptions()) + .windowsOptions(ctx.getWindowsOptions()); + + } + /** * get if pod is in ready state. * @param pod pod @@ -474,7 +485,10 @@ EffectiveServerSpec getServerSpec() { @Override V1SecurityContext getInitContainerSecurityContext() { - return PodSecurityHelper.getDefaultContainerSecurityContext(); + if (getPodSecurityContext().equals(PodSecurityHelper.getDefaultPodSecurityContext())) { + return PodSecurityHelper.getDefaultContainerSecurityContext(); + } + return getEffectiveSecurityContext(getPodSecurityContext()); } @Override @@ -757,7 +771,10 @@ protected List getContainerCommand() { @Override V1SecurityContext getInitContainerSecurityContext() { - return PodSecurityHelper.getDefaultContainerSecurityContext(); + if (getPodSecurityContext().equals(PodSecurityHelper.getDefaultPodSecurityContext())) { + return PodSecurityHelper.getDefaultContainerSecurityContext(); + } + return getEffectiveSecurityContext(getPodSecurityContext()); } @Override From 68ae818810ec94938da46a7a63c6398cbb4502a6 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Feb 2025 17:42:17 -0500 Subject: [PATCH 274/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 4a85faa5f0b..93e475767fc 100644 --- a/pom.xml +++ b/pom.xml @@ -673,7 +673,7 @@ 21 [3.8.1,) 3.5.0 - 3.4.0 + 3.4.1 3.13.0 3.1.3 3.1.3 @@ -700,7 +700,7 @@ 1.9.0 1.5.5 1.4.0 - 1.17.1 + 1.18.0 1.7.3 0.1.0 2.9.0 @@ -728,9 +728,9 @@ 0.16.0 2.18.2 2.18.2 - 2.3 + 2.4 2.12.1 - 12.0.2 + 12.1.0 2.0.16 1.5.16 4.29.3 From 49d65dbb58e0441e02b6f5ac814d6ef87b04982e Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Feb 2025 18:17:30 -0500 Subject: [PATCH 275/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 93e475767fc..03f7ed835e8 100644 --- a/pom.xml +++ b/pom.xml @@ -710,7 +710,7 @@ 3.27.3 2.18.0 4.2.2 - 19.0.1 + 19.0.2 3.0.1u2 2.1.10 4.12.0 From 78ba3c86b964fa9ce753c85731f54e94e3b2be20 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Feb 2025 18:32:12 -0500 Subject: [PATCH 276/356] Prepare for WKO 4.2.15 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index e50f26da7cf..9778552b4db 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 41b28319c8e..336e7c16342 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 0c735f31e3e..bbbcb9e7ec3 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 2198ed6a338..dda3bd43930 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.15-SNAPSHOT + 4.2.15 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 0fe9f501c8c..108dcb3cd6b 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 7cbc9392498..0a8ee7f7c3c 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.15-SNAPSHOT + 4.2.15 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 591f224ad70..f41b0b4c52a 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 03f7ed835e8..ef1f97dc070 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 4238c2c302f..53cce89b6fe 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15-SNAPSHOT + 4.2.15 operator-swagger From 0c9a931d39ee41cfbce19a7326cd509389287009 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Feb 2025 19:11:36 -0500 Subject: [PATCH 277/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 9778552b4db..36292a9904a 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 336e7c16342..a99ec674ccd 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index bbbcb9e7ec3..c2febf60f68 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index dda3bd43930..10586b0a3d4 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.15 + 4.2.16-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 108dcb3cd6b..6f017aa7ece 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 0a8ee7f7c3c..fc80bf3d0e4 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.15 + 4.2.16-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index f41b0b4c52a..f3d036add6b 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index ef1f97dc070..683deee3664 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 53cce89b6fe..843fce4cda9 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.15 + 4.2.16-SNAPSHOT operator-swagger From 47afbfae43869cd13bdee27d4b71cc6d8c7ec6b9 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 21 Feb 2025 20:24:57 +0000 Subject: [PATCH 278/356] test changes needed in 4.2 for 14.1.2 images --- Jenkinsfile | 8 ++--- Jenkinsfile.kindnightly | 2 +- Jenkinsfile.podman | 8 ++--- .../ItFmwDomainInPvUserCreateRcu.java | 14 +++------ .../kubernetes/ItIntrospectVersion.java | 12 +++++++- .../ItIstioGatewaySessionMigration.java | 30 +++++++++++++++---- .../kubernetes/ItIstioSessionMigration.java | 12 +++++++- .../kubernetes/ItLBTwoDomainsNginx.java | 13 +++++++- .../kubernetes/ItLBTwoDomainsTraefik.java | 19 +++++++++++- .../ItMonitoringExporterWebApp.java | 8 +++++ .../kubernetes/ItSessionMigration.java | 8 +++++ .../kubernetes/ItSystemResOverrides.java | 13 +++++++- .../weblogic/kubernetes/ItT3Channel.java | 12 ++++++-- .../weblogic/kubernetes/TestConstants.java | 8 ++--- .../extensions/InitializationTasks.java | 2 +- .../kubernetes/utils/CommonTestUtils.java | 2 +- .../weblogic/kubernetes/utils/DbUtils.java | 5 ++-- .../weblogic/kubernetes/utils/DeployUtil.java | 26 ++++++++++++++-- .../kubernetes/utils/DomainUtils.java | 3 +- .../kubernetes/utils/MonitoringUtils.java | 2 +- .../resources/istio/istio-http-template.yaml | 4 +-- .../python-scripts/application_deployment.py | 3 +- .../wdt-models/model-dci-introspect.yaml | 2 +- .../wdt-models/sitconfig-dci-model.yaml | 3 ++ 24 files changed, 170 insertions(+), 49 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d94c2f442ee..fe8f7a42db3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -143,16 +143,16 @@ pipeline { defaultValue: "test-images/weblogic" ) string(name: 'WEBLOGIC_IMAGE_TAG', - description: '12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', - defaultValue: '12.2.1.4' + description: '14.1.2.0-generic-jdk17-ol8, 14.1.2.0-generic-jdk17-ol9, 14.1.2.0-generic-jdk21-ol8, 14.1.2.0-generic-jdk21-ol9, 12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '14.1.2.0-generic-jdk17-ol8' ) string(name: 'FMWINFRA_IMAGE_NAME', description: 'FWM Infra image name. Default is the image name in BASE_IMAGES_REPO. Use middleware/fmw-infrastructure for OCR.', defaultValue: "test-images/fmw-infrastructure" ) string(name: 'FMWINFRA_IMAGE_TAG', - description: 'FWM Infra image tag', - defaultValue: '12.2.1.4' + description: '14.1.2.0-jdk17-ol8, 14.1.2.0-jdk17-ol9, 14.1.2.0-jdk21-ol8, 14.1.2.0-jdk21-ol9', + defaultValue: '14.1.2.0-jdk17-ol8' ) string(name: 'DB_IMAGE_NAME', description: 'Oracle DB image name. Default is the image name in BASE_IMAGES_REPO, use database/enterprise for OCR.', diff --git a/Jenkinsfile.kindnightly b/Jenkinsfile.kindnightly index 63588ddcdae..6ddc8737ac4 100644 --- a/Jenkinsfile.kindnightly +++ b/Jenkinsfile.kindnightly @@ -152,7 +152,7 @@ pipeline { defaultValue: "${env.WKT_OCIR_HOST}" ) choice(name: 'BASE_IMAGES_REPO', - choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], + choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], description: 'Repository to pull the base images. Make sure to modify the image names if you are modifying this parameter value.' ) string(name: 'WEBLOGIC_IMAGE_NAME', diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index eabf6553331..d604c4dfe4b 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -153,16 +153,16 @@ pipeline { defaultValue: "test-images/weblogic" ) string(name: 'WEBLOGIC_IMAGE_TAG', - description: '12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', - defaultValue: '12.2.1.4' + description: '14.1.2.0-generic-jdk17-ol8, 14.1.2.0-generic-jdk17-ol9, 14.1.2.0-generic-jdk21-ol8, 14.1.2.0-generic-jdk21-ol9, 12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '14.1.2.0-generic-jdk17-ol8' ) string(name: 'FMWINFRA_IMAGE_NAME', description: 'FWM Infra image name. Default is the image name in BASE_IMAGES_REPO. Use middleware/fmw-infrastructure for OCR.', defaultValue: "test-images/fmw-infrastructure" ) string(name: 'FMWINFRA_IMAGE_TAG', - description: 'FWM Infra image tag', - defaultValue: '12.2.1.4' + description: '14.1.2.0-jdk17-ol8, 14.1.2.0-jdk17-ol9, 14.1.2.0-jdk21-ol8, 14.1.2.0-jdk21-ol9', + defaultValue: '14.1.2.0-jdk17-ol8' ) string(name: 'DB_IMAGE_NAME', description: 'Oracle DB image name. Default is the image name in BASE_IMAGES_REPO, use database/enterprise for OCR.', diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java index 991bb42996e..0989a3a0db4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainInPvUserCreateRcu.java @@ -38,7 +38,6 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; -import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.ELASTICSEARCH_HOST; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.MII_AUXILIARY_IMAGE_NAME; @@ -60,11 +59,11 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.addSccToDBSvcAccount; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createCustomConditionFactory; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.verifyConfiguredSystemResource; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; +import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; import static oracle.weblogic.kubernetes.utils.DbUtils.startOracleDB; @@ -132,6 +131,8 @@ public class ItFmwDomainInPvUserCreateRcu { private final String opsswalletfileSecretName1 = domainUid1 + "-opss-wallet-file-secret"; private final String opsswalletfileSecretName3 = domainUid3 + "-opss-wallet-file-secret"; private final String opsswalletfileSecretName4 = domainUid4 + "-opss-wallet-file-secret"; + private static String dbName = "my-orcl-sidb"; + private static final String RCUSYSPASSWORD = "Oradoc_db1"; private static final int replicaCount = 1; private final String fmwModelFilePrefix = "model-fmwdomainonpv-rcu-wdt"; @@ -154,9 +155,6 @@ public static void initAll(@Namespaces(3) List namespaces) { logger.info("Assign a unique namespace for DB and RCU"); assertNotNull(namespaces.get(0), "Namespace is null"); dbNamespace = namespaces.get(0); - final int dbListenerPort = getNextFreePort(); - ORACLEDBSUFFIX = ".svc.cluster.local:" + dbListenerPort + "/devpdb.k8s"; - dbUrl = ORACLEDBURLPREFIX + dbNamespace + ORACLEDBSUFFIX; // get a new unique opNamespace logger.info("Assign a unique namespace for operator1"); @@ -169,11 +167,7 @@ public static void initAll(@Namespaces(3) List namespaces) { domainNamespace = namespaces.get(2); // start DB - logger.info("Start DB in namespace: {0}, dbListenerPort: {1}, dbUrl: {2}, dbImage: {3}", - dbNamespace, dbListenerPort, dbUrl, DB_IMAGE_TO_USE_IN_SPEC); - assertDoesNotThrow(() -> setupDB(DB_IMAGE_TO_USE_IN_SPEC, dbNamespace, getNextFreePort(), dbListenerPort), - String.format("Failed to setup DB in the namespace %s with dbUrl %s, dbListenerPost %s", - dbNamespace, dbUrl, dbListenerPort)); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, RCUSYSPASSWORD, dbNamespace)); // install operator with DomainOnPvSimplification=true" HelmParams opHelmParams = diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index b847ce00e93..a850eb95fc5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -87,6 +87,7 @@ import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.ITTESTS_DIR; @@ -223,7 +224,16 @@ class ItIntrospectVersion { private static Path clusterViewAppPath; private static LoggingFacade logger = null; - private static final int managedServerPort = 7100; + private static int managedServerPort; + + static { + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + managedServerPort = 7100; + } else { + managedServerPort = 7001; + } + } + private static int adminPort = 7001; private static String hostHeader; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java index fb3002dc20d..b7b6e738daa 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioGatewaySessionMigration.java @@ -3,6 +3,7 @@ package oracle.weblogic.kubernetes; +import java.net.UnknownHostException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -28,6 +29,7 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.addLabelsToNamespace; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.checkAppUsingHostHeader; @@ -90,6 +92,12 @@ class ItIstioGatewaySessionMigration { private static int istioIngressPort = 0; private static String testWebAppWarLoc = null; private static int managedServerPort = 7100; + + static { + if (!WEBLOGIC_IMAGE_TAG.startsWith("12")) { + managedServerPort = 7001; + } + } private static final String istioNamespace = "istio-system"; private static final String istioIngressServiceName = "istio-ingressgateway"; @@ -171,7 +179,7 @@ public static void initAll(@Namespaces(2) List namespaces) { @Test @DisplayName("When istio is enabled using Istio gateway, stop the primary server, " + "verify that a new primary server is picked and HTTP session state is migrated") - void testSessionMigrationIstioGateway() { + void testSessionMigrationIstioGateway() throws UnknownHostException { final String primaryServerAttr = "primary"; final String secondaryServerAttr = "secondary"; final String sessionCreateTimeAttr = "sessioncreatetime"; @@ -183,7 +191,13 @@ void testSessionMigrationIstioGateway() { // In internal OKE env, use Istio EXTERNAL-IP; in non-OKE env, use K8S_NODEPORT_HOST + ":" + istioIngressPort String istioIngressIP = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) : formatIPv6Host(K8S_NODEPORT_HOST); - + int servicePort = istioIngressPort; + if (TestConstants.KIND_CLUSTER + && !TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { + istioIngressIP = domainUid + "-cluster-cluster-1." + domainNamespace + ".svc.cluster.local"; + servicePort = 7001; + } + // send a HTTP request to set http session state(count number) and save HTTP session info // before shutting down the primary server // the NodePort services created by the operator are not usable, because they would expose ports @@ -191,7 +205,7 @@ void testSessionMigrationIstioGateway() { Map httpDataInfo = OKE_CLUSTER ? getServerAndSessionInfoAndVerify(domainNamespace, adminServerPodName, serverName, istioIngressIP, 0, webServiceSetUrl, " -c ") : getServerAndSessionInfoAndVerify(domainNamespace, - adminServerPodName, serverName, istioIngressIP, istioIngressPort, webServiceSetUrl, " -c "); + adminServerPodName, serverName, istioIngressIP, servicePort, webServiceSetUrl, " -c "); // get server and session info from web service deployed on the cluster String origPrimaryServerName = httpDataInfo.get(primaryServerAttr); String origSecondaryServerName = httpDataInfo.get(secondaryServerAttr); @@ -209,7 +223,7 @@ void testSessionMigrationIstioGateway() { httpDataInfo = OKE_CLUSTER ? getServerAndSessionInfoAndVerify(domainNamespace, adminServerPodName, serverName, istioIngressIP, 0, webServiceGetUrl, " -b ") : getServerAndSessionInfoAndVerify(domainNamespace, - adminServerPodName, serverName, istioIngressIP, istioIngressPort, webServiceGetUrl, " -b "); + adminServerPodName, serverName, istioIngressIP, servicePort, webServiceGetUrl, " -b "); // get server and session info from web service deployed on the cluster String primaryServerName = httpDataInfo.get(primaryServerAttr); String sessionCreateTime = httpDataInfo.get(sessionCreateTimeAttr); @@ -247,11 +261,15 @@ private static int configIstioGatewayModelInImageDomain(String miiImage, String clusterService = domainUid + "-cluster-" + clusterName + "." + domainNamespace + ".svc.cluster.local"; - Map templateMap = new HashMap<>(); + Map templateMap = new HashMap<>(); templateMap.put("NAMESPACE", domainNamespace); templateMap.put("DUID", domainUid); - templateMap.put("ADMIN_SERVICE",adminServerPodName); + templateMap.put("ADMIN_SERVICE", adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + if (!WEBLOGIC_IMAGE_TAG.startsWith("12")) { + templateMap.put("7100", String.valueOf(managedServerPort)); + templateMap.put("8001", String.valueOf(managedServerPort)); + } // create Istio gateway Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", istioGatewayConfigFile); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java index f60ae815648..4166488bf03 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioSessionMigration.java @@ -21,6 +21,7 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.actions.TestActions.addLabelsToNamespace; import static oracle.weblogic.kubernetes.utils.CommonMiiTestUtils.configIstioModelInImageDomain; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.generateNewModelFileWithUpdatedDomainUid; @@ -62,7 +63,16 @@ class ItIstioSessionMigration { private static String clusterName = "cluster-1"; private static String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; private static String managedServerPrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; - private static int managedServerPort = 7100; + private static int managedServerPort; + + static { + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + managedServerPort = 7100; + } else { + managedServerPort = 7001; + } + } + private static String finalPrimaryServerName = null; private static String configMapName = "istio-configmap"; private static int replicaCount = 2; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java index 1f8806e3d09..2019ba90b0a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsNginx.java @@ -43,6 +43,7 @@ import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE_DIR; import static oracle.weblogic.kubernetes.TestConstants.SKIP_CLEANUP; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.TestActions.createIngress; @@ -94,7 +95,16 @@ class ItLBTwoDomainsNginx { // domain constants private static final int replicaCount = 2; - private static final int MANAGED_SERVER_PORT = 7100; + private static int MANAGED_SERVER_PORT; + + static { + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + MANAGED_SERVER_PORT = 7100; + } else { + MANAGED_SERVER_PORT = 7001; + } + } + private static final int ADMIN_SERVER_PORT = 7001; private static final String clusterName = "cluster-1"; @@ -289,6 +299,7 @@ private static void createNginxIngressHostRoutingForTwoDomains(String ingressCla List tlsList = new ArrayList<>(); for (String domainUid : domainUids) { + V1HTTPIngressPath httpIngressPath = new V1HTTPIngressPath() .path(null) .pathType("ImplementationSpecific") diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java index 3c92e99cef1..eb77d4e37dc 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLBTwoDomainsTraefik.java @@ -39,6 +39,7 @@ import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_NODEPORT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.TestActions.deletePersistentVolume; @@ -85,7 +86,16 @@ class ItLBTwoDomainsTraefik { // domain constants private static final int replicaCount = 2; - private static final int MANAGED_SERVER_PORT = 7100; + private static int MANAGED_SERVER_PORT; + + static { + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + MANAGED_SERVER_PORT = 7100; + } else { + MANAGED_SERVER_PORT = 7001; + } + } + private static final int ADMIN_SERVER_PORT = 7001; private static final String clusterName = "cluster-1"; @@ -267,9 +277,16 @@ private static void createTraefikIngressRoutingRules(String domainNamespace) { assertDoesNotThrow(() -> { Files.deleteIfExists(dstFile); Files.createDirectories(dstFile.getParent()); + String msPort; + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + msPort = "7100"; + } else { + msPort = "7001"; + } Files.write(dstFile, Files.readString(srcFile).replaceAll("@NS@", domainNamespace) .replaceAll("@domain1uid@", domainUids.get(0)) .replaceAll("@domain2uid@", domainUids.get(1)) + .replaceAll("7100", msPort) .getBytes(StandardCharsets.UTF_8)); }); String command = KUBERNETES_CLI + " create -f " + dstFile; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java index 4eac6a553c7..60257f361dc 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterWebApp.java @@ -33,6 +33,7 @@ import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.ExecResult; +import oracle.weblogic.kubernetes.utils.LoggingUtil; import oracle.weblogic.kubernetes.utils.MonitoringUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -153,6 +154,7 @@ class ItMonitoringExporterWebApp { private static String grafanaReleaseName = "grafana" + releaseSuffix; private static String monitoringExporterDir; private static String hostPortPrometheus = null; + private static List ns; /** @@ -167,6 +169,7 @@ class ItMonitoringExporterWebApp { public static void initAll(@Namespaces(6) List namespaces) throws UnknownHostException { logger = getLogger(); + ns = namespaces; monitoringExporterDir = Paths.get(RESULTS_ROOT, "ItMonitoringExporterWebApp", "monitoringexp").toString(); monitoringExporterSrcDir = Paths.get(monitoringExporterDir, "srcdir").toString(); @@ -390,6 +393,7 @@ void testAdminPortEnabled() throws Exception { @DisplayName("Test Monitoring Exporter access to metrics via https.") void testAccessExporterViaHttps() throws Exception { String miiImage1 = null; + boolean collectLogs = true; try { logger.info("create and verify WebLogic domain image using model in image with model files for norestport"); @@ -427,7 +431,11 @@ void testAccessExporterViaHttps() throws Exception { true, clusterName), "monitoring exporter metrics page can't be accessed via https"); }); + collectLogs = false; } finally { + if (collectLogs) { + LoggingUtil.generateLog(this, ns); + } logger.info("Shutting down domain3"); shutdownDomain(domain3Uid, domain3Namespace); if (miiImage1 != null) { diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java index 83611e4427b..8adf005878a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSessionMigration.java @@ -39,6 +39,7 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.utils.ClusterUtils.createClusterResourceAndAddReferenceToDomain; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.generateNewModelFileWithUpdatedDomainUid; @@ -98,6 +99,13 @@ class ItSessionMigration { // does not explicitly specify ListenPort, the introspector/wdt generated // default ListenPort for each dynamic server is set to 7100 private static int managedServerPort = 7100; + + static { + if (!WEBLOGIC_IMAGE_TAG.startsWith("12")) { + managedServerPort = 7001; + } + } + private static int replicaCount = 2; private static String opNamespace = null; private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index 814603e648b..7c3ddf713e0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -60,6 +60,7 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; @@ -128,9 +129,18 @@ class ItSystemResOverrides { final String adminServerName = "admin-server"; final String adminServerPodName = domainUid + "-" + adminServerName; final String managedServerNameBase = "ms-"; - final int managedServerPort = 8001; + private static int managedServerPort = 8001; int t3ChannelPort; private static int adminPort = 7001; + + static { + if (WEBLOGIC_IMAGE_TAG_DEFAULT.startsWith("14")) { + adminPort = 7001; + } else { + adminPort = 7002; + } + } + private static String hostHeader; final String pvName = getUniqueName(domainUid + "-pv-"); final String pvcName = getUniqueName(domainUid + "-pvc-"); @@ -466,6 +476,7 @@ private void createDomain() { + "-Dweblogic.debug.DebugSituationalConfigDumpXml=true " + "-Dweblogic.kernel.debug=true " + "-Dweblogic.debug.DebugMessaging=true " + + "-Dweblogic.security.SSL.ignoreHostnameVerification=true " + "-Dweblogic.debug.DebugConnection=true " + "-Dweblogic.ResolveDNSName=true")) .addEnvItem(new V1EnvVar() diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java index 9717a359b56..f52d9db2141 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItT3Channel.java @@ -52,7 +52,6 @@ import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; -import static oracle.weblogic.kubernetes.TestConstants.DEFAULT_LISTEN_PORT; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS; @@ -63,6 +62,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; @@ -329,7 +329,13 @@ void testAdminServerT3Channel() { -> getServicePort(domainNamespace, domainUid + "-cluster-" + clusterName, "default"), "Getting Cluster Service default port failed"); - assertEquals(DEFAULT_LISTEN_PORT, servicePort, "Default Service Port is not set to 7100"); + int managedServerPort; + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + managedServerPort = 7100; + } else { + managedServerPort = 7001; + } + assertEquals(managedServerPort, servicePort, "Default Service Port is not set to " + managedServerPort); int napPort = assertDoesNotThrow(() -> getServicePort(domainNamespace, domainUid + "-cluster-" + clusterName, "ms-nap"), @@ -340,7 +346,7 @@ void testAdminServerT3Channel() { -> getServicePort(domainNamespace, domainUid + "-managed-server1", "default"), "Getting Managed Server Service default port failed"); - assertEquals(DEFAULT_LISTEN_PORT, servicePort, "Default Managed Service Port is not 7100"); + assertEquals(managedServerPort, servicePort, "Default Managed Service Port is not " + managedServerPort); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 51f8fa86c3b..be2a5baa57e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -107,13 +107,13 @@ public interface TestConstants { // It depends on the default value of BASE_IMAGES_REPO. // Following defaults are assumining OCIR as default for BASE_IMAGES_REPO. public static final String WEBLOGIC_IMAGE_NAME_DEFAULT = "test-images/weblogic"; - public static final String WEBLOGIC_IMAGE_TAG_DEFAULT = "12.2.1.4"; + public static final String WEBLOGIC_IMAGE_TAG_DEFAULT = "14.1.2.0-generic-jdk17-ol8"; public static final String FMWINFRA_IMAGE_NAME_DEFAULT = "test-images/fmw-infrastructure"; - public static final String FMWINFRA_IMAGE_TAG_DEFAULT = "12.2.1.4"; + public static final String FMWINFRA_IMAGE_TAG_DEFAULT = "14.1.2.0-jdk17-ol8"; public static final String FMWINFRA_IMAGE_TAG_12213 = "12.2.1.3"; public static final String DB_IMAGE_NAME_DEFAULT = "test-images/database/enterprise"; public static final String DB_PREBUILT_IMAGE_NAME_DEFAULT = "test-images/database/express"; - public static final String DB_IMAGE_TAG_DEFAULT = "12.2.0.1-slim"; + public static final String DB_IMAGE_TAG_DEFAULT = "19.3.0.0"; // repository to push the domain images created during test execution // (a) for kind cluster push to kind repo @@ -361,7 +361,7 @@ public interface TestConstants { public static final String DEFAULT_EXTERNAL_REST_IDENTITY_SECRET_NAME = "weblogic-operator-external-rest-identity"; public static final String ISTIO_VERSION = - getNonEmptySystemProperty("wko.it.istio.version", "1.13.2"); + getNonEmptySystemProperty("wko.it.istio.version", "1.23.0"); public static final int ISTIO_HTTP_HOSTPORT = 2480; public static final int ISTIO_HTTPS_HOSTPORT = 2490; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index ed6e243a72a..59d8ccd1cc3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.extensions; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java index 25784179215..a55e23d49d6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonTestUtils.java @@ -197,7 +197,7 @@ public static ConditionFactory createCustomConditionFactory(int polldelay, int p */ public static void testUntil(Callable conditionEvaluator, LoggingFacade logger, String msg, Object... params) { - testUntil(withStandardRetryPolicy, conditionEvaluator, logger, msg, params); + testUntil(withLongRetryPolicy, conditionEvaluator, logger, msg, params); } /** diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java index de2613dd55d..e0d58412a84 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DbUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -837,7 +837,7 @@ public static String createOracleDBUsingOperator(String dbName, String sysPasswo String namespace) throws ApiException, IOException { LoggingFacade logger = getLogger(); - final String DB_IMAGE_19C = DB_IMAGE_NAME + ":" + DB_19C_IMAGE_TAG; + final String DB_IMAGE_19C = DB_IMAGE_NAME + ":" + DB_19C_IMAGE_TAG; String secretName = "db-password"; String secretKey = "password"; Map secretMap = new HashMap<>(); @@ -850,6 +850,7 @@ public static String createOracleDBUsingOperator(String dbName, String sysPasswo assertTrue(secretCreated, String.format("create secret failed for %s", secretName)); createTestRepoSecret(namespace); + createBaseRepoSecret(namespace); final String pvName = getUniqueName(dbName + "-pv"); createPV(pvName); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java index f8c3ddac0e9..7cf0479c3d1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DeployUtil.java @@ -58,7 +58,7 @@ public class DeployUtil { private static final String MOUNT_POINT = "/deployScripts/"; private static final String DEPLOY_SCRIPT = "application_deployment.py"; private static final String DOMAIN_PROPERTIES = "domain.properties"; - + /** * Deploy application. * @@ -71,7 +71,24 @@ public class DeployUtil { * @param namespace name of the namespace in which WebLogic server pods running */ public static void deployUsingWlst(String host, String port, String userName, - String password, String targets, Path archivePath, String namespace) { + String password, String targets, Path archivePath, String namespace) { + deployUsingWlst(host, port, userName, password, targets, archivePath, namespace, false); + } + + /** + * Deploy application. + * + * @param host name of the admin server host + * @param port default channel node port of admin server + * @param userName admin server user name + * @param password admin server password + * @param targets comma separated list of targets to deploy applications + * @param archivePath local path of the application archive + * @param namespace name of the namespace in which WebLogic server pods running + * @param secure admin server running in secure mode or not + */ + public static void deployUsingWlst(String host, String port, String userName, String password, + String targets, Path archivePath, String namespace, boolean secure) { final LoggingFacade logger = getLogger(); // this secret is used only for non-kind cluster @@ -88,6 +105,11 @@ public static void deployUsingWlst(String host, String port, String userName, p.setProperty("admin_username", userName); p.setProperty("admin_password", password); p.setProperty("targets", targets); + if (secure) { + p.setProperty("protocol", "t3s"); + } else { + p.setProperty("protocol", "t3"); + } assertDoesNotThrow(() -> p.store(new FileOutputStream(domainPropertiesFile), "wlst properties file"), "Failed to write the domain properties to file"); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java index a8de8825dde..649fc45c73d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/DomainUtils.java @@ -1067,7 +1067,8 @@ public static DomainResource createDomainResourceOnPv(String domainUid, .serverPod(new ServerPod() //serverpod .addEnvItem(new V1EnvVar() .name("JAVA_OPTIONS") - .value("-Dweblogic.StdoutDebugEnabled=false")) + .value("-Dweblogic.StdoutDebugEnabled=false " + + "-Dweblogic.security.SSL.ignoreHostnameVerification=true")) .addEnvItem(new V1EnvVar() .name("USER_MEM_ARGS") .value("-Djava.security.egd=file:/dev/./urandom")) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index d9f11fcb628..dc610e5de5a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -1317,7 +1317,7 @@ public static boolean verifyMonExpAppAccess(String uri, String searchKey, String String port = "8001"; if (isHttps) { protocol = "https"; - port = "8100"; + port = "7002"; } String podName = domainUid + "-" + clusterName + "-managed-server1"; if (clusterName == null) { diff --git a/integration-tests/src/test/resources/istio/istio-http-template.yaml b/integration-tests/src/test/resources/istio/istio-http-template.yaml index b7978006d0a..9ee6eb499da 100644 --- a/integration-tests/src/test/resources/istio/istio-http-template.yaml +++ b/integration-tests/src/test/resources/istio/istio-http-template.yaml @@ -47,9 +47,9 @@ spec: prefix: /myear - uri: prefix: /testwebapp - - port: 8001 + - port: 7001 route: - destination: host: CLUSTER_SERVICE port: - number: 8001 + number: 7001 diff --git a/integration-tests/src/test/resources/python-scripts/application_deployment.py b/integration-tests/src/test/resources/python-scripts/application_deployment.py index e6f64929c08..1ad220fd917 100644 --- a/integration-tests/src/test/resources/python-scripts/application_deployment.py +++ b/integration-tests/src/test/resources/python-scripts/application_deployment.py @@ -18,9 +18,10 @@ print 'admin_password: ' + admin_password print 'targets: ' + targets print 'mounted archive: ' + node_archive_path +print 'protocol: ' + protocol -t3url = "t3://" + admin_host + ":" + admin_port +t3url = protocol + "://" + admin_host + ":" + admin_port print 't3url: ' + t3url archive_name = os.path.basename(node_archive_path) diff --git a/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml b/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml index 1533b993fcf..c0c28d24459 100644 --- a/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml +++ b/integration-tests/src/test/resources/wdt-models/model-dci-introspect.yaml @@ -1,10 +1,10 @@ domainInfo: AdminUserName: '@@PROP:WebLogicAdminUserName@@' AdminPassword: '@@PROP:WebLogicAdminPassword@@' + ServerStartMode: 'prod' topology: Name: mydomain AdminServerName: admin-server - ProductionModeEnabled: true Cluster: mycluster: WeblogicPluginEnabled: true diff --git a/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml b/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml index 0120d24fabe..a4d419b9f5a 100644 --- a/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml +++ b/integration-tests/src/test/resources/wdt-models/sitconfig-dci-model.yaml @@ -5,6 +5,9 @@ topology: Name: mysitconfigdomain AdminServerName: admin-server ProductionModeEnabled: true + SecurityConfiguration: + SecureMode: + SecureModeEnabled: false Cluster: mycluster: DynamicServers: From 4e6c076db239e179ecea59a128563dd8783d52cf Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 24 Feb 2025 15:16:50 -0500 Subject: [PATCH 279/356] Dependency updates --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 683deee3664..d0f8c6fb95c 100644 --- a/pom.xml +++ b/pom.xml @@ -674,7 +674,7 @@ [3.8.1,) 3.5.0 3.4.1 - 3.13.0 + 3.14.0 3.1.3 3.1.3 3.4.2 @@ -688,7 +688,7 @@ 3.8.1 3.6.0 3.5.0 - 10.21.2 + 10.21.3 1.0 3.6.0 3.2.7 @@ -698,7 +698,7 @@ 2.0.1 1.0.39 1.9.0 - 1.5.5 + 1.5.6 1.4.0 1.18.0 1.7.3 @@ -709,14 +709,14 @@ 1.0.0 3.27.3 2.18.0 - 4.2.2 + 4.3.0 19.0.2 3.0.1u2 2.1.10 4.12.0 3.10.2 1.80 - 5.11.4 + 5.12.0 5.7.1 1.7.0 1.3.2 From eddbd9ccb8a1b3e61b0074bfd441d50390854c42 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 26 Feb 2025 11:41:53 -0500 Subject: [PATCH 280/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d0f8c6fb95c..3d505e942e1 100644 --- a/pom.xml +++ b/pom.xml @@ -731,11 +731,11 @@ 2.4 2.12.1 12.1.0 - 2.0.16 - 1.5.16 + 2.0.17 + 1.5.17 4.29.3 2.5.2 - 10.0.1 + 10.0.2 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 550417b3b48013ef647757cddfe30501f41cce7c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 27 Feb 2025 16:55:48 -0500 Subject: [PATCH 281/356] Use WME 2.2.3 --- documentation/domains/Domain.json | 4 ++-- documentation/domains/Domain.md | 2 +- kubernetes/crd/cluster-crd.yaml | 2 +- kubernetes/crd/domain-crd.yaml | 8 ++++---- .../oracle/kubernetes/operator/KubernetesConstants.java | 4 ++-- .../oracle/kubernetes/operator/helpers/CrdHelper.java | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index c5ac2c2d532..def02193ab0 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -880,8 +880,8 @@ "type": "object", "properties": { "image": { - "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2", - "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2", + "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3", + "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3", "type": "string" }, "imagePullPolicy": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index fe5f5a87c56..6d87fdd7f77 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -145,7 +145,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | Name | Type | Description | | --- | --- | --- | | `configuration` | Map | The configuration for the WebLogic Monitoring Exporter. If WebLogic Server instances are already running and have the monitoring exporter sidecar container, then changes to this field will be propagated to the exporter without requiring the restart of the WebLogic Server instances. | -| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2 | +| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3 | | `imagePullPolicy` | string | The image pull policy for the WebLogic Monitoring Exporter sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. | | `port` | integer | The port exposed by the WebLogic Monitoring Exporter running in the sidecar container. Defaults to 8080. The port value must not conflict with a port used by any WebLogic Server instance, including the ports of built-in channels or network access points (NAPs). | | `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. | diff --git a/kubernetes/crd/cluster-crd.yaml b/kubernetes/crd/cluster-crd.yaml index 4157548288e..7684b9f6d1e 100644 --- a/kubernetes/crd/cluster-crd.yaml +++ b/kubernetes/crd/cluster-crd.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. apiVersion: apiextensions.k8s.io/v1 diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index b822f178f3b..24f7502e626 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -1,11 +1,11 @@ -# Copyright (c) 2020, 2023, Oracle and/or its affiliates. +# Copyright (c) 2020, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 7a1a4cb0613c9b7b8e8478d852cf4b239f718496ff389d02317c00e49cffd6ec + weblogic.sha256: d2fb5c512aeefca074e3d56c99bca6ffd4ff9df3dc021dd6f7471b51f0f4c212 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -47,9 +47,9 @@ spec: appropriate. See https://github.com/oracle/weblogic-monitoring-exporter. properties: image: - default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2 + default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3 description: The WebLogic Monitoring Exporter sidecar container - image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2 + image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3 type: string imagePullPolicy: description: The image pull policy for the WebLogic Monitoring diff --git a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java index d61821e5af7..56fce5c4023 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2024, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -8,7 +8,7 @@ /** Kubernetes constants. */ public interface KubernetesConstants { String DEFAULT_IMAGE = "container-registry.oracle.com/middleware/weblogic:12.2.1.4"; - String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.2"; + String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3"; String DEFAULT_FLUENTD_IMAGE = "fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2"; String EXPORTER_CONTAINER_NAME = "monitoring-exporter"; String LATEST_IMAGE_SUFFIX = ":latest"; diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java index 3f4ce2fd15a..f6d10720ce1 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -137,7 +137,7 @@ static void writeAsYaml(URI outputFileName, Object model) { try (Writer writer = Files.newBufferedWriter(PathSupport.getPath(outputFileName))) { writer.write( """ - # Copyright (c) 2020, 2023, Oracle and/or its affiliates. + # Copyright (c) 2020, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. """); writer.write("\n"); From 2340f3fb56d2c6264e4bd2bf5f2a1e054362c361 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 3 Mar 2025 16:31:12 -0500 Subject: [PATCH 282/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 3d505e942e1..7c41a942330 100644 --- a/pom.xml +++ b/pom.xml @@ -675,8 +675,8 @@ 3.5.0 3.4.1 3.14.0 - 3.1.3 - 3.1.3 + 3.1.4 + 3.1.4 3.4.2 3.3.1 3.21.0 @@ -726,8 +726,8 @@ 4.0.2 6.1.0 0.16.0 - 2.18.2 - 2.18.2 + 2.18.3 + 2.18.3 2.4 2.12.1 12.1.0 From 5e9d74af5244e26618893e72ae036dfd21572b18 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Tue, 4 Mar 2025 19:35:03 +0000 Subject: [PATCH 283/356] Fix domain delayed response --- .../operator/DomainProcessorImpl.java | 8 +- .../kubernetes/operator/LabelConstants.java | 1 + .../operator/helpers/PodHelper.java | 87 +++++++++++++++++++ .../steps/ShutdownManagedServerStep.java | 21 ++--- .../operator/DomainProcessorTest.java | 1 + .../operator/helpers/PodPresenceTest.java | 18 +--- .../steps/ShutdownManagedServerStepTest.java | 10 ++- 7 files changed, 117 insertions(+), 29 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java b/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java index 3a6f8a8cdd6..12770777117 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java @@ -601,7 +601,8 @@ private void processServerPodWatch(V1Pod pod, String watchType) { // fall through case MODIFIED: boolean podPreviouslyEvicted = info.setServerPodFromEvent(serverName, pod, PodHelper::isEvicted); - if (PodHelper.isEvicted(pod) && !podPreviouslyEvicted) { + boolean isEvicted = PodHelper.isEvicted(pod); + if (isEvicted && !podPreviouslyEvicted) { if (PodHelper.shouldRestartEvictedPod(pod)) { LOGGER.info(MessageKeys.POD_EVICTED, getPodName(pod), getPodStatusMessage(pod)); createMakeRightOperation(info).interrupt().withExplicitRecheck().execute(); @@ -609,6 +610,11 @@ private void processServerPodWatch(V1Pod pod, String watchType) { LOGGER.info(MessageKeys.POD_EVICTED_NO_RESTART, getPodName(pod), getPodStatusMessage(pod)); } } + boolean isReady = PodHelper.isReady(pod); + boolean isLabeledForShutdown = PodHelper.isPodAlreadyAnnotatedForShutdown(pod); + if ((isEvicted || isReady != isLabeledForShutdown || PodHelper.isFailed(pod)) && !PodHelper.isDeleting(pod)) { + createMakeRightOperation(info).interrupt().withExplicitRecheck().execute(); + } boolean isUnschedulable = PodHelper.hasUnSchedulableCondition(pod); if (isUnschedulable) { LOGGER.info(POD_UNSCHEDULABLE, getPodName(pod), getUnSchedulableConditionMessage(pod)); diff --git a/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java b/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java index 9a49e26be06..7886c8da4cb 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java @@ -25,6 +25,7 @@ public interface LabelConstants { String MII_UPDATED_RESTART_REQUIRED_LABEL = "weblogic.configChangesPendingRestart"; String INTROSPECTION_DOMAIN_SPEC_GENERATION = "weblogic.domainSpecGeneration"; String TO_BE_ROLLED_LABEL = "weblogic.awaitingPodRoll"; + String TO_BE_SHUTDOWN_LABEL = "weblogic.awaitingShutdown"; String DOMAIN_OBSERVED_GENERATION_LABEL = "weblogic.domainObservedGeneration"; String CLUSTER_OBSERVED_GENERATION_LABEL = "weblogic.clusterObservedGeneration"; String SERVICE_TYPE_LABEL = "serviceType"; diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java index 5bffe971766..1422434b8b6 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -48,6 +49,7 @@ import oracle.kubernetes.weblogic.domain.model.ServerStatus; import oracle.kubernetes.weblogic.domain.model.Shutdown; +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static oracle.kubernetes.operator.KubernetesConstants.EVICTED_REASON; import static oracle.kubernetes.operator.KubernetesConstants.POD_SCHEDULED; import static oracle.kubernetes.operator.KubernetesConstants.UNSCHEDULABLE_REASON; @@ -56,6 +58,7 @@ import static oracle.kubernetes.operator.ProcessingConstants.SERVERS_TO_ROLL; import static oracle.kubernetes.operator.WebLogicConstants.SHUTDOWN_STATE; import static oracle.kubernetes.operator.WebLogicConstants.UNKNOWN_STATE; +import static oracle.kubernetes.operator.helpers.PodDisruptionBudgetHelper.getDomainUid; @SuppressWarnings("ConstantConditions") public class PodHelper { @@ -128,6 +131,7 @@ public static boolean isWaitingToRoll(V1Pod pod) { .orElse(false); } + static boolean hasReadyServer(V1Pod pod) { return Optional.ofNullable(pod).map(PodHelper::hasReadyStatus).orElse(false); } @@ -393,6 +397,89 @@ public static String getPodLabel(V1Pod pod, String labelName) { .orElse(null); } + + /** + * get pod's annotation value for a annotation name. + * @param pod pod + * @param annotationName annotation name + * @return annotation value + */ + public static String getPodAnnotation(V1Pod pod, String annotationName) { + return Optional.ofNullable(pod) + .map(V1Pod::getMetadata) + .map(V1ObjectMeta::getAnnotations) + .map(m -> m.get(annotationName)) + .orElse(null); + } + + private static boolean hasAnnotation(V1Pod pod, String annotation) { + return Optional.ofNullable(pod).map(V1Pod::getMetadata).map(V1ObjectMeta::getAnnotations) + .map(l -> l.containsKey(annotation)).orElse(false); + } + + private static Step patchPodAnnotation(V1Pod pod, String annotation, String value, Step next) { + + if (!hasAnnotation(pod, annotation)) { + JsonPatchBuilder patchBuilder = Json.createPatchBuilder(); + patchBuilder.add("/metadata/annotations/" + annotation, value); + new CallBuilder() + .patchPodAsync(pod.getMetadata().getName(), pod.getMetadata().getNamespace(), + pod.getMetadata().getLabels().get(LabelConstants.DOMAINUID_LABEL), + new V1Patch(patchBuilder.build().toString()), + patchResponse(next)); + } + return next; + } + + private static ResponseStep patchResponse(Step next) { + return new PatchPodResponseStep(next); + } + + private static class PatchPodResponseStep extends ResponseStep { + PatchPodResponseStep(Step next) { + super(next); + } + + @Override + public NextAction onSuccess(Packet packet, CallResponse callResponse) { + DomainPresenceInfo info = packet.getSpi(DomainPresenceInfo.class); + V1Pod pod = callResponse.getResult(); + info.setServerPod(getPodServerName(pod), pod); + return doNext(packet); + } + + @Override + public NextAction onFailure(Packet packet, CallResponse callResponse) { + if (callResponse.getStatusCode() == HTTP_NOT_FOUND) { + return doNext(packet); + } + return super.onFailure(packet, callResponse); + } + } + + /** + * Annotate pod as needing to shut down. + * @param pod Pod + * @param next Next step + * @return Step that will check for existing annotation and add if it is missing + */ + public static Step annotatePodAsNeedingToShutdown(V1Pod pod, String value, Step next) { + return patchPodAnnotation(pod, LabelConstants.TO_BE_SHUTDOWN_LABEL, value, next); + } + + /** + * Check if the pod is already annotated for shut down. + * @param pod Pod + * @return true, if the pod is already annotated. + */ + public static boolean isPodAlreadyAnnotatedForShutdown(V1Pod pod) { + return !Objects.isNull(getPodShutdownAnnotation(pod)); + } + + public static String getPodShutdownAnnotation(V1Pod pod) { + return getPodAnnotation(pod, LabelConstants.TO_BE_SHUTDOWN_LABEL); + } + /** * Get the message from the pod's status. * @param pod pod diff --git a/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java b/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java index d74ad7117c6..317ae80f4b1 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java @@ -5,6 +5,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.time.OffsetDateTime; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -66,9 +67,9 @@ private ShutdownManagedServerStep(Step next, String serverName, V1Pod pod) { /** * Creates asynchronous {@link Step}. * - * @param next Next processing step + * @param next Next processing step * @param serverName name of server - * @param pod server pod + * @param pod server pod * @return asynchronous step */ static Step createShutdownManagedServerStep(Step next, String serverName, V1Pod pod) { @@ -80,15 +81,15 @@ public NextAction apply(Packet packet) { LOGGER.fine(MessageKeys.BEGIN_SERVER_SHUTDOWN_REST, serverName); V1Service service = getDomainPresenceInfo(packet).getServerService(serverName); - if (service == null) { - return doNext(packet); - } else { - return doNext( - Step.chain( - SecretHelper.createAuthorizationSourceStep(), - new ShutdownManagedServerWithHttpStep(service, pod, getNext())), - packet); + String now = OffsetDateTime.now().toString(); + if (service == null || !PodHelper.isReady(pod) || PodHelper.isFailed(pod) || PodHelper.isWaitingToRoll(pod)) { + return doNext(PodHelper.annotatePodAsNeedingToShutdown(pod, now, getNext()), packet); } + return doNext( + Step.chain( + SecretHelper.createAuthorizationSourceStep(), + PodHelper.annotatePodAsNeedingToShutdown(pod, now, + new ShutdownManagedServerWithHttpStep(service, pod, getNext()))), packet); } static final class ShutdownManagedServerProcessing extends HttpRequestProcessing { diff --git a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java index 75e5c67b2ea..1f6e70efa38 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java @@ -728,6 +728,7 @@ void afterMakeRightAndChangeServerToNever_serverPodsWaitForShutdownWithHttpToCom domainConfigurator.withDefaultServerStartPolicy(ServerStartPolicy.NEVER); DomainStatus status = newInfo.getDomain().getStatus(); defineServerShutdownWithHttpOkResponse(); + makePodsReady(); setAdminServerStatus(status, SUSPENDING_STATE); setManagedServerState(status, SUSPENDING_STATE); diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java index 99a38ad0cb3..889a36c5165 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java @@ -517,23 +517,7 @@ void onModifyEventWithEvictedServerPod_cycleServerPod() { assertThat(numPodsDeleted, is(1)); assertThat(createdPodNames, hasItem(SERVER)); } - - @Test - void onModifyEventWithEvictedServerPod_dontCycleAlreadyEvictedServerPod() { - V1Pod currentPod = withEvictedStatus(createServerPod()); - V1Pod modifiedPod = withEvictedStatus(createServerPod()); - List createdPodNames = new ArrayList<>(); - testSupport.doOnCreate(POD, p -> recordPodCreation((V1Pod) p, createdPodNames)); - - info.setServerPod(SERVER, currentPod); - Watch.Response event = WatchEvent.createModifiedEvent(modifiedPod).toWatchResponse(); - - processor.dispatchPodWatch(event); - - assertThat(numPodsDeleted, is(0)); - assertThat(createdPodNames, not(hasItem(SERVER))); - } - + @Test void onModifyEventWithEvictedServerPod_notCycleServerPod_ifConfiguredNotTo() { TuningParametersStub.setParameter("restartEvictedPods", "false"); diff --git a/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java b/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java index c7ae817b4be..bf80866ee9f 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java @@ -18,7 +18,9 @@ import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Pod; +import io.kubernetes.client.openapi.models.V1PodCondition; import io.kubernetes.client.openapi.models.V1PodSpec; +import io.kubernetes.client.openapi.models.V1PodStatus; import io.kubernetes.client.openapi.models.V1Service; import oracle.kubernetes.operator.DomainProcessorTestSetup; import oracle.kubernetes.operator.KubernetesConstants; @@ -198,7 +200,13 @@ private V1Pod createPod(String serverName) { List env = addShutdownEnvVars(); List containers = addEnvToWLSContainer(env); V1PodSpec podSpec = new V1PodSpec().containers(containers); - return new V1Pod().metadata(createManagedPodMetadata(serverName)).spec(podSpec); + return new V1Pod().metadata(createManagedPodMetadata(serverName)).spec(podSpec).status(createPodReadyStatus()); + } + + private V1PodStatus createPodReadyStatus() { + return new V1PodStatus() + .phase("Running") + .addConditionsItem(new V1PodCondition().status("True").type("Ready")); } @Nonnull From c4c42b735bff4cd149cda53acfac75a87c7276ec Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Thu, 6 Mar 2025 18:31:52 +0000 Subject: [PATCH 284/356] kind sequential job fix --- .../kubernetes/ItFmwDomainOnPVUpgrade.java | 48 ++++++++++++++++--- .../kubernetes/ItMultiDomainModelsScale.java | 4 +- .../kubernetes/utils/ApplicationUtils.java | 2 +- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java index 86cb7a83739..1a8ddf32faa 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVUpgrade.java @@ -12,6 +12,7 @@ import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -44,6 +45,7 @@ import oracle.weblogic.kubernetes.assertions.impl.Cluster; import oracle.weblogic.kubernetes.logging.LoggingFacade; import oracle.weblogic.kubernetes.utils.ExecResult; +import org.awaitility.core.ConditionFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; @@ -51,10 +53,13 @@ import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; -import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_TENANCY; import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; @@ -62,9 +67,9 @@ import static oracle.weblogic.kubernetes.TestConstants.ELASTICSEARCH_HOST; import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_INTERVAL_SECONDS; import static oracle.weblogic.kubernetes.TestConstants.FAILURE_RETRY_LIMIT_MINUTES; -import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_NAME_DEFAULT; -import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.KIND_REPO; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.OPERATOR_CHART_DIR; @@ -76,6 +81,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.execCommand; import static oracle.weblogic.kubernetes.actions.TestActions.getDomainCustomResource; import static oracle.weblogic.kubernetes.actions.TestActions.imagePull; +import static oracle.weblogic.kubernetes.actions.TestActions.imagePush; import static oracle.weblogic.kubernetes.actions.TestActions.imageTag; import static oracle.weblogic.kubernetes.actions.impl.Domain.shutdown; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podDoesNotExist; @@ -103,6 +109,7 @@ import static oracle.weblogic.kubernetes.utils.SecretUtils.createOpsswalletpasswordSecret; import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.awaitility.Awaitility.with; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -133,8 +140,15 @@ class ItFmwDomainOnPVUpgrade { private final String fmwModelFilePrefix = "model-fmwdomain-upgrade"; - private final String imageTag1412 = "14.1.2.0.0-jdk17"; - private final String image1412 = BASE_IMAGES_PREFIX + FMWINFRA_IMAGE_NAME_DEFAULT + ":" + imageTag1412; + private static final String imageTag12214 = "12.2.1.4"; + private static final String image12214 = FMWINFRA_IMAGE_NAME + ":" + imageTag12214; + private static final String imageTag1412 = "14.1.2.0-jdk17-ol8"; + private static final String image1412 = FMWINFRA_IMAGE_NAME + ":" + imageTag1412; + + private static ConditionFactory withVeryLongRetryPolicy + = with().pollDelay(0, SECONDS) + .and().with().pollInterval(10, SECONDS) + .atMost(30, MINUTES).await(); /** * Assigns unique namespaces for DB, operator and domain. @@ -189,6 +203,21 @@ public static void initAll(@Namespaces(3) List namespaces) { // create pull secrets for domainNamespace when running in non Kind Kubernetes cluster // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); + + if (KIND_REPO != null) { + Collection images = new ArrayList<>(); + images.add(image12214); + images.add(image1412); + + for (String image : images) { + testUntil( + withVeryLongRetryPolicy, + pullImageFromBaseRepoAndPushToKind(image), + logger, + "pullImageFromBaseRepoAndPushToKind for image {0} to be successful", + image); + } + } } /** @@ -342,7 +371,7 @@ private void createDomain(String domainName, String startMode, String rcuSchemap DOMAINHOMEPREFIX, replicaCount, configuration, - FMWINFRA_IMAGE_TO_USE_IN_SPEC); + image12214); // Set the inter-pod anti-affinity for the domain custom resource setPodAntiAffinity(domain); @@ -587,4 +616,11 @@ private static DomainResource createDomainResourceOnPv(String domainUid, return domain; } + private static Callable pullImageFromBaseRepoAndPushToKind(String image) { + return (() -> { + String kindRepoImage = KIND_REPO + image.substring(BASE_IMAGES_REPO.length() + BASE_IMAGES_TENANCY.length() + 2); + return imagePull(image) && imageTag(image, kindRepoImage) && imagePush(kindRepoImage); + }); + } + } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java index 8a4a53a0d6b..f9ab3281f0d 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMultiDomainModelsScale.java @@ -3,6 +3,7 @@ package oracle.weblogic.kubernetes; +import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -461,7 +462,8 @@ void testScaleClustersWithWLDF(String domainType) { } else if (!WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT)) { hostHeader = createIngressHostRoutingIfNotExists(domainNamespace, domainUid); assertDoesNotThrow(() - -> verifyAdminServerRESTAccess("localhost", TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); + -> verifyAdminServerRESTAccess(InetAddress.getLocalHost().getHostAddress(), + TRAEFIK_INGRESS_HTTP_HOSTPORT, false, hostHeader)); } else { verifyReadyAppUsingAdminNodePort(domainUid, domainNamespace); // verify admin console login using ingress controller diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java index b10a6bfb82a..09f5a46a200 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ApplicationUtils.java @@ -494,7 +494,7 @@ public static void verifyAdminConsoleAccessible(String domainNamespace, public static boolean verifyAdminServerRESTAccess(String host, int port, boolean secure, String hostHeader) throws IOException { getLogger().info("Check REST interface availability"); - StringBuffer curlCmd = new StringBuffer("status=$(curl -vkg --noproxy '*'"); + StringBuffer curlCmd = new StringBuffer("status=$(curl -vkg --noproxy '*' -m 30"); if (host.contains(":")) { host = "[" + host + "]"; } From 26fc896f56dd35bfa94fd373a71f93403dad7f44 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Thu, 6 Mar 2025 18:35:02 +0000 Subject: [PATCH 285/356] Support init container for introspector pod --- documentation/domains/Domain.json | 7 + documentation/domains/Domain.md | 1 + kubernetes/crd/domain-crd.yaml | 541 +++++++++++++++++- .../operator/helpers/JobStepContext.java | 10 + .../EffectiveIntrospectorJobPodSpec.java | 3 + ...ctiveIntrospectorJobPodSpecCommonImpl.java | 7 + .../domain/model/IntrospectorJobPod.java | 28 + .../IntrospectorJobPodConfiguration.java | 5 + 8 files changed, 601 insertions(+), 1 deletion(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index def02193ab0..99ece30e5ae 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -782,6 +782,13 @@ "items": { "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.EnvFromSource" } + }, + "initContainers": { + "description": "List of init containers for the introspector Job Pod. These containers run after the auxiliary image init container. See `kubectl explain pods.spec.initContainers`.", + "type": "array", + "items": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Container" + } } } }, diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 6d87fdd7f77..ade21ecde3b 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -278,6 +278,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | --- | --- | --- | | `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the Introspector Job Pod container. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. | | `envFrom` | Array of [Env From Source](k8s1.28.2.md#env-from-source) | List of sources to populate environment variables in the Introspector Job Pod container. The sources include either a config map or a secret. The operator will not expand the dependent variables in the 'envFrom' source. More details: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container. Also see: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. | +| `initContainers` | Array of [Container](k8s1.28.2.md#container) | List of init containers for the introspector Job Pod. These containers run after the auxiliary image init container. See `kubectl explain pods.spec.initContainers`. | | `podSecurityContext` | [Pod Security Context](k8s1.28.2.md#pod-security-context) | Pod-level security attributes. See `kubectl explain pods.spec.securityContext`. If no value is specified for this field, the operator will use default content for the pod-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Introspector Job Pod. See `kubectl explain pods.spec.containers.resources`. | diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 24f7502e626..1030e79915d 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: d2fb5c512aeefca074e3d56c99bca6ffd4ff9df3dc021dd6f7471b51f0f4c212 + weblogic.sha256: 91dd1108f8152681e0392cb7ffbe57497e2f76456051d6a7f037ee04ae832110 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -4312,6 +4312,545 @@ spec: optional: type: boolean type: array + initContainers: + description: List of init containers for the introspector + Job Pod. These containers run after the auxiliary image + init container. See `kubectl explain pods.spec.initContainers`. + items: + type: object + properties: + volumeDevices: + type: array + items: + type: object + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + image: + type: string + imagePullPolicy: + type: string + livenessProbe: + type: object + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + type: object + properties: + command: + type: array + items: + type: string + grpc: + type: object + properties: + port: + type: integer + service: + type: string + required: + - port + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port + stdin: + type: boolean + terminationMessagePolicy: + type: string + terminationMessagePath: + type: string + workingDir: + type: string + resources: + type: object + properties: + claims: + type: array + items: + type: object + properties: + name: + type: string + required: + - name + requests: + additionalProperties: + type: string + type: object + limits: + additionalProperties: + type: string + type: object + securityContext: + type: object + properties: + privileged: + type: boolean + runAsUser: + type: integer + capabilities: + type: object + properties: + add: + type: array + items: + type: string + drop: + type: array + items: + type: string + seLinuxOptions: + type: object + properties: + role: + type: string + level: + type: string + type: + type: string + user: + type: string + seccompProfile: + type: object + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + windowsOptions: + type: object + properties: + gmsaCredentialSpec: + type: string + runAsUserName: + type: string + hostProcess: + type: boolean + gmsaCredentialSpecName: + type: string + procMount: + type: string + allowPrivilegeEscalation: + type: boolean + runAsGroup: + type: integer + runAsNonRoot: + type: boolean + readOnlyRootFilesystem: + type: boolean + startupProbe: + type: object + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + type: object + properties: + command: + type: array + items: + type: string + grpc: + type: object + properties: + port: + type: integer + service: + type: string + required: + - port + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port + env: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + valueFrom: + type: object + properties: + secretKeyRef: + type: object + properties: + name: + type: string + optional: + type: boolean + key: + type: string + required: + - key + resourceFieldRef: + type: object + properties: + divisor: + type: string + resource: + type: string + containerName: + type: string + required: + - resource + configMapKeyRef: + type: object + properties: + name: + type: string + optional: + type: boolean + key: + type: string + required: + - key + fieldRef: + type: object + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + required: + - name + ports: + type: array + items: + type: object + properties: + protocol: + type: string + hostIP: + type: string + name: + type: string + containerPort: + type: integer + hostPort: + type: integer + required: + - containerPort + command: + type: array + items: + type: string + volumeMounts: + type: array + items: + type: object + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + args: + type: array + items: + type: string + lifecycle: + type: object + properties: + postStart: + type: object + properties: + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + exec: + type: object + properties: + command: + type: array + items: + type: string + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port + preStop: + type: object + properties: + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + exec: + type: object + properties: + command: + type: array + items: + type: string + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port + name: + type: string + tty: + type: boolean + readinessProbe: + type: object + properties: + terminationGracePeriodSeconds: + type: integer + failureThreshold: + type: integer + periodSeconds: + type: integer + tcpSocket: + type: object + properties: + port: + x-kubernetes-int-or-string: true + host: + type: string + required: + - port + timeoutSeconds: + type: integer + successThreshold: + type: integer + initialDelaySeconds: + type: integer + exec: + type: object + properties: + command: + type: array + items: + type: string + grpc: + type: object + properties: + port: + type: integer + service: + type: string + required: + - port + httpGet: + type: object + properties: + path: + type: string + scheme: + type: string + port: + x-kubernetes-int-or-string: true + host: + type: string + httpHeaders: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + required: + - port + resizePolicy: + type: array + items: + type: object + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + stdinOnce: + type: boolean + envFrom: + type: array + items: + type: object + properties: + configMapRef: + type: object + properties: + name: + type: string + optional: + type: boolean + prefix: + type: string + secretRef: + type: object + properties: + name: + type: string + optional: + type: boolean + required: + - name + type: array type: object type: object clusters: diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 351ff2bd714..355e25f2f1c 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -189,6 +189,11 @@ private List getIntrospectorEnvVariables() { .map(EffectiveIntrospectorJobPodSpec::getEnv).orElse(new ArrayList<>()); } + private List getIntrospectorInitContainers() { + return Optional.ofNullable(getDomain().getIntrospectorSpec()) + .map(EffectiveIntrospectorJobPodSpec::getInitContainers).orElse(new ArrayList<>()); + } + private List getAdminServerEnvVariables() { return Optional.ofNullable(getDomain().getAdminServerSpec()).map(EffectiveServerSpec::getEnvironmentVariables) .orElse(new ArrayList<>()); @@ -470,6 +475,11 @@ private void addInitContainers(List initContainers, List initContainers.add(createInitContainerForAuxiliaryImage(auxiliaryImages.get(idx), idx, isInitializeDomainOnPV()))); + List introspectorInitContainers = getIntrospectorInitContainers(); + for (V1Container initContainer : introspectorInitContainers) { + initContainer.securityContext(getInitContainerSecurityContext()); + initContainers.add(initContainer); + } } private Optional getInitializeDomainOnPV() { diff --git a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveIntrospectorJobPodSpec.java b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveIntrospectorJobPodSpec.java index 09dcf1bed70..78bcd6be99f 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveIntrospectorJobPodSpec.java +++ b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveIntrospectorJobPodSpec.java @@ -5,6 +5,7 @@ import java.util.List; +import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1PodSecurityContext; @@ -28,4 +29,6 @@ public interface EffectiveIntrospectorJobPodSpec { V1ResourceRequirements getResources(); V1PodSecurityContext getPodSecurityContext(); + + List getInitContainers(); } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveIntrospectorJobPodSpecCommonImpl.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveIntrospectorJobPodSpecCommonImpl.java index 01dd4fa8597..37751b9b632 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveIntrospectorJobPodSpecCommonImpl.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveIntrospectorJobPodSpecCommonImpl.java @@ -5,6 +5,7 @@ import java.util.List; +import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1PodSecurityContext; @@ -48,4 +49,10 @@ public V1ResourceRequirements getResources() { public V1PodSecurityContext getPodSecurityContext() { return introspector.getPodSecurityContext(); } + + @Override + public List getInitContainers() { + return introspector.getInitContainers(); + } + } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java index d3aa4fad213..11cc36c1a4a 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPod.java @@ -11,6 +11,7 @@ import javax.annotation.Nullable; import io.kubernetes.client.custom.Quantity; +import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1PodSecurityContext; @@ -74,6 +75,16 @@ class IntrospectorJobPod { + "More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/.") private V1PodSecurityContext podSecurityContext = null; + /** + * InitContainers holds a list of initialization containers that run after the auxiliary image init + * container for the introspector job pod. + * + */ + @Valid + @Description("List of init containers for the introspector Job Pod. These containers run after the auxiliary image " + + "init container. See `kubectl explain pods.spec.initContainers`.") + private List initContainers = null; + private static void copyValues(V1ResourceRequirements to, V1ResourceRequirements from) { if (from != null) { if (from.getRequests() != null) { @@ -133,6 +144,12 @@ void fillInFrom(IntrospectorJobPod serverPod1) { } envFrom.addAll(serverPod1.envFrom); } + if (serverPod1.initContainers != null) { + if (initContainers == null) { + initContainers = new ArrayList<>(); + } + initContainers.addAll(serverPod1.initContainers); + } copyValues(resources, serverPod1.resources); copyValues(serverPod1.podSecurityContext); } @@ -147,6 +164,14 @@ private List getV1EnvVars() { return Optional.ofNullable(getEnv()).orElse(emptyList()); } + List getInitContainers() { + return Optional.ofNullable(initContainers).orElse(emptyList()); + } + + void setInitContainers(@Nullable List initContainers) { + this.initContainers = initContainers; + } + private boolean hasEnvVar(String name) { if (env == null) { return false; @@ -210,6 +235,7 @@ public String toString() { .append("resources", resources) .append("envFrom", envFrom) .append("podSecurityContext", podSecurityContext) + .append("initContainers", initContainers) .toString(); } @@ -232,6 +258,7 @@ public boolean equals(Object o) { .append(resources, that.resources) .append(envFrom, that.envFrom) .append(podSecurityContext, that.podSecurityContext) + .append(initContainers, that.initContainers) .isEquals(); } @@ -243,6 +270,7 @@ public int hashCode() { .append(resources) .append(envFrom) .append(podSecurityContext) + .append(initContainers) .toHashCode(); } } \ No newline at end of file diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPodConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPodConfiguration.java index ae355106146..67509edf1ab 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPodConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/IntrospectorJobPodConfiguration.java @@ -6,6 +6,7 @@ import java.util.List; import javax.annotation.Nullable; +import io.kubernetes.client.openapi.models.V1Container; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1PodSecurityContext; @@ -48,6 +49,10 @@ public void setEnv(@Nullable List env) { serverPod.setEnv(env); } + public List getInitContainers() { + return serverPod.getInitContainers(); + } + void addEnvironmentVariable(String name, String value) { serverPod.addEnvVar(new V1EnvVar().name(name).value(value)); } From a66b5269051e1c62f15ef556fb3df025517cc4a4 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 6 Mar 2025 13:52:27 -0500 Subject: [PATCH 286/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7c41a942330..0f022b5247b 100644 --- a/pom.xml +++ b/pom.xml @@ -688,7 +688,7 @@ 3.8.1 3.6.0 3.5.0 - 10.21.3 + 10.21.4 1.0 3.6.0 3.2.7 @@ -733,7 +733,7 @@ 12.1.0 2.0.17 1.5.17 - 4.29.3 + 4.30.0 2.5.2 10.0.2 ${project.basedir}/src-generated-swagger From b406da8dfb9dab38a936134c24cca97126ddc5c9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 13 Mar 2025 15:39:48 -0400 Subject: [PATCH 287/356] Use WME 2.3.0 --- documentation/domains/Domain.json | 4 ++-- documentation/domains/Domain.md | 2 +- kubernetes/crd/domain-crd.yaml | 6 +++--- .../oracle/kubernetes/operator/KubernetesConstants.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index 99ece30e5ae..ebf49cd8c73 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -887,8 +887,8 @@ "type": "object", "properties": { "image": { - "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3", - "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3", + "default": "ghcr.io/oracle/weblogic-monitoring-exporter:2.3.0", + "description": "The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.3.0", "type": "string" }, "imagePullPolicy": { diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index ade21ecde3b..77b4de1cb75 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -145,7 +145,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | Name | Type | Description | | --- | --- | --- | | `configuration` | Map | The configuration for the WebLogic Monitoring Exporter. If WebLogic Server instances are already running and have the monitoring exporter sidecar container, then changes to this field will be propagated to the exporter without requiring the restart of the WebLogic Server instances. | -| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3 | +| `image` | string | The WebLogic Monitoring Exporter sidecar container image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.3.0 | | `imagePullPolicy` | string | The image pull policy for the WebLogic Monitoring Exporter sidecar container image. Legal values are Always, Never, and IfNotPresent. Defaults to Always if image ends in :latest; IfNotPresent, otherwise. | | `port` | integer | The port exposed by the WebLogic Monitoring Exporter running in the sidecar container. Defaults to 8080. The port value must not conflict with a port used by any WebLogic Server instance, including the ports of built-in channels or network access points (NAPs). | | `resources` | [Resource Requirements](k8s1.28.2.md#resource-requirements) | Memory and CPU minimum requirements and limits for the Monitoring exporter sidecar. See `kubectl explain pods.spec.containers.resources`. | diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 1030e79915d..88e1655a5e1 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 91dd1108f8152681e0392cb7ffbe57497e2f76456051d6a7f037ee04ae832110 + weblogic.sha256: deb0c93b1b3e11669640a91830463ef0e6c8a5288a2d1c902e6f8a3c42d421b0 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -47,9 +47,9 @@ spec: appropriate. See https://github.com/oracle/weblogic-monitoring-exporter. properties: image: - default: ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3 + default: ghcr.io/oracle/weblogic-monitoring-exporter:2.3.0 description: The WebLogic Monitoring Exporter sidecar container - image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3 + image name. Defaults to ghcr.io/oracle/weblogic-monitoring-exporter:2.3.0 type: string imagePullPolicy: description: The image pull policy for the WebLogic Monitoring diff --git a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java index 56fce5c4023..e3a0c3c6ba4 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/KubernetesConstants.java @@ -8,7 +8,7 @@ /** Kubernetes constants. */ public interface KubernetesConstants { String DEFAULT_IMAGE = "container-registry.oracle.com/middleware/weblogic:12.2.1.4"; - String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.2.3"; + String DEFAULT_EXPORTER_IMAGE = "ghcr.io/oracle/weblogic-monitoring-exporter:2.3.0"; String DEFAULT_FLUENTD_IMAGE = "fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch7-1.2"; String EXPORTER_CONTAINER_NAME = "monitoring-exporter"; String LATEST_IMAGE_SUFFIX = ":latest"; From 0dad180cc4febbdad129b37947f16fa89b038ee1 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Mon, 17 Mar 2025 17:56:36 +0000 Subject: [PATCH 288/356] Parallel nightly test failure fixes --- Jenkinsfile.podman | 2 +- .../ItDiagnosticsFailedCondition.java | 39 ++++++++----------- .../kubernetes/ItIstioDBOperator.java | 1 + .../kubernetes/ItIstioDomainInImage.java | 1 + .../kubernetes/ItIstioDomainInPV.java | 1 + .../weblogic/kubernetes/ItIstioMiiDomain.java | 1 + .../kubernetes/ItIstioMonitoringExporter.java | 1 + .../kubernetes/ItIstioTwoDomainsInImage.java | 1 + .../ItLiftAndShiftFromOnPremDomain.java | 2 + .../kubernetes/ItMiiCustomSslStore.java | 13 +++++-- .../kubernetes/ItOpenshiftIstioMiiDomain.java | 1 + .../weblogic/kubernetes/ItPodTemplates.java | 3 ++ .../resources/istio/istio-http-template.yaml | 4 +- 13 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index d604c4dfe4b..891182a7ba7 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -170,7 +170,7 @@ pipeline { ) string(name: 'DB_IMAGE_TAG', description: 'Oracle DB image tag', - defaultValue: '12.2.0.1-slim' + defaultValue: '19.3.0.0' ) string(name: 'MONITORING_EXPORTER_BRANCH', description: '', diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java index 85a9b7cb6de..21781582129 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItDiagnosticsFailedCondition.java @@ -13,6 +13,7 @@ import javax.annotation.Nonnull; import io.kubernetes.client.custom.V1Patch; +import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1ObjectMeta; @@ -29,7 +30,6 @@ import oracle.weblogic.domain.Model; import oracle.weblogic.domain.ServerPod; import oracle.weblogic.domain.ServerService; -import oracle.weblogic.kubernetes.actions.impl.primitive.Kubernetes; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; import oracle.weblogic.kubernetes.logging.LoggingFacade; @@ -48,7 +48,6 @@ import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO; import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.CLUSTER_VERSION; -import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_AVAILABLE_TYPE; import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_STATUS_CONDITION_COMPLETED_TYPE; @@ -84,8 +83,10 @@ import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.configMapExist; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapFromFiles; +import static oracle.weblogic.kubernetes.utils.DbUtils.createOracleDBUsingOperator; import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuAccessSecret; -import static oracle.weblogic.kubernetes.utils.DbUtils.setupDBandRCUschema; +import static oracle.weblogic.kubernetes.utils.DbUtils.createRcuSchema; +import static oracle.weblogic.kubernetes.utils.DbUtils.deleteOracleDB; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeExists; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkDomainStatusConditionTypeHasExpectedStatus; import static oracle.weblogic.kubernetes.utils.DomainUtils.checkServerStatusPodPhaseAndPodReady; @@ -734,24 +735,21 @@ void testMSBootFailureStatus() { String rcuaccessSecretName = domainName + "-rcu-access"; String opsswalletpassSecretName = domainName + "-opss-wallet-password-secret"; + String rcuSysPassword = "Oradoc_db1"; + String dbName = domainName + "my-oracle-db"; - logger.info("Start DB and create RCU schema for namespace: {0}, dbListenerPort: {1}, RCU prefix: {2}, " - + "dbUrl: {3}, dbImage: {4}, fmwImage: {5} ", domainNamespace, dbListenerPort, rcuSchemaPrefix, dbUrl, - DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC); - assertDoesNotThrow(() -> setupDBandRCUschema(DB_IMAGE_TO_USE_IN_SPEC, FMWINFRA_IMAGE_TO_USE_IN_SPEC, - rcuSchemaPrefix, domainNamespace, getNextFreePort(), dbUrl, dbListenerPort), - String.format("Failed to create RCU schema for prefix %s in the namespace %s with " - + "dbUrl %s, dbListenerPost %s", rcuSchemaPrefix, domainNamespace, dbUrl, dbListenerPort)); + logger.info("Create Oracle DB in namespace: {0} ", domainNamespace); + createBaseRepoSecret(domainNamespace); + dbUrl = assertDoesNotThrow(() -> createOracleDBUsingOperator(dbName, rcuSysPassword, domainNamespace)); + + logger.info("Create RCU schema with fmwImage: {0}, rcuSchemaPrefix: {1}, dbUrl: {2}, " + + " dbNamespace: {3}", FMWINFRA_IMAGE_TO_USE_IN_SPEC, rcuSchemaPrefix, dbUrl, domainNamespace); + createRcuSchema(FMWINFRA_IMAGE_TO_USE_IN_SPEC, rcuSchemaPrefix, dbUrl, domainNamespace); // create RCU access secret logger.info("Creating RCU access secret: {0}, with prefix: {1}, dbUrl: {2}, schemapassword: {3})", rcuaccessSecretName, rcuSchemaPrefix, rcuSchemaPassword, dbUrl); - assertDoesNotThrow(() -> createRcuAccessSecret(rcuaccessSecretName, - domainNamespace, - rcuSchemaPrefix, - rcuSchemaPassword, - dbUrl), - String.format("createSecret failed for %s", rcuaccessSecretName)); + createRcuAccessSecret(rcuaccessSecretName, domainNamespace, rcuSchemaPrefix, rcuSchemaPassword, dbUrl); logger.info("Create OPSS wallet password secret"); assertDoesNotThrow(() -> createOpsswalletpasswordSecret( @@ -823,12 +821,7 @@ rcuSchemaPrefix, domainNamespace, getNextFreePort(), dbUrl, dbListenerPort), } // delete Oracle database - String dbPodName = "oracledb"; - assertDoesNotThrow(() -> Kubernetes.deleteDeployment(domainNamespace, "oracledb"), - "deleting oracle db failed"); - - logger.info("Wait for the oracle Db pod: {0} to be deleted in namespace {1}", dbPodName, domainNamespace); - PodUtils.checkPodDeleted(dbPodName, null, domainNamespace); + deleteOracleDB(domainNamespace, dbName); patchStr = "[{\"op\": \"replace\", \"path\": \"/spec/serverStartPolicy\", \"value\": \"IfNeeded\"}]"; @@ -848,6 +841,8 @@ rcuSchemaPrefix, domainNamespace, getNextFreePort(), dbUrl, dbListenerPort), checkServerStatusPodPhaseAndPodReady(domainName, domainNamespace, managedServerName, "Running", "False"); } testPassed = true; + } catch (ApiException ex) { + logger.severe(ex.getLocalizedMessage()); } finally { if (!testPassed) { LoggingUtil.generateLog(this, ns); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java index a819302f3a6..a2daeafcfe0 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDBOperator.java @@ -539,6 +539,7 @@ private int enableIstio(String clusterName, String domainUid, String namespace, templateMap.put("DUID", domainUid); templateMap.put("ADMIN_SERVICE", adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + templateMap.put("MANAGED_SERVER_PORT", "7001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java index a62f6618f77..afd13b4b5d6 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInImage.java @@ -183,6 +183,7 @@ void testIstioDomainHomeInImage() throws UnknownHostException { templateMap.put("DUID", domainUid); templateMap.put("ADMIN_SERVICE",adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + templateMap.put("MANAGED_SERVER_PORT", "8001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java index cbef7c27ecf..100550f8a08 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioDomainInPV.java @@ -319,6 +319,7 @@ void testIstioDomainHomeInPv() throws UnknownHostException { templateMap.put("DUID", domainUid); templateMap.put("ADMIN_SERVICE",adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + templateMap.put("MANAGED_SERVER_PORT", "8001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java index 6f8673b947f..d97b79c77af 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java @@ -215,6 +215,7 @@ void testIstioModelInImageDomain() throws UnknownHostException, IOException, Int templateMap.put("DUID", domainUid); templateMap.put("ADMIN_SERVICE", adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + templateMap.put("MANAGED_SERVER_PORT", "7001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java index d60a459b44d..cb398abc85e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMonitoringExporter.java @@ -379,6 +379,7 @@ private void setupIstioModelInImageDomain(String miiImage, String domainNamespac templateMap.put("DUID", domainUid); templateMap.put("ADMIN_SERVICE",adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + templateMap.put("MANAGED_SERVER_PORT", "7001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java index eef5918f85c..02148ebf3e2 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioTwoDomainsInImage.java @@ -233,6 +233,7 @@ void testIstioTwoDomainsWithSingleIngress() throws UnknownHostException { templateMap.put("DUID", domainUid1); templateMap.put("ADMIN_SERVICE",adminServerPodName1); templateMap.put("CLUSTER_SERVICE", clusterService1); + templateMap.put("MANAGED_SERVER_PORT", "8001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java index c505cb88eb2..9862a58eeea 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItLiftAndShiftFromOnPremDomain.java @@ -30,6 +30,7 @@ import oracle.weblogic.kubernetes.utils.ExecCommand; import oracle.weblogic.kubernetes.utils.ExecResult; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -108,6 +109,7 @@ @Tag("oke-sequential") @IntegrationTest +@Disabled class ItLiftAndShiftFromOnPremDomain { private static String traefikNamespace = null; private static String domainNamespace = null; diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java index c4eba945e3c..9ae7fced093 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMiiCustomSslStore.java @@ -23,6 +23,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; import static oracle.weblogic.kubernetes.actions.TestActions.scaleAllClustersInDomain; @@ -217,12 +218,18 @@ private void runClientOnAdminPod() { StringBuffer extOpts = new StringBuffer(""); extOpts.append("-Dweblogic.security.SSL.ignoreHostnameVerification=true "); extOpts.append("-Dweblogic.security.SSL.trustedCAKeyStore=/shared/" - + domainNamespace + "/" + domainUid + "/TrustKeyStore.jks "); + + domainNamespace + "/" + domainUid + "/TrustKeyStore.jks "); extOpts.append("-Dweblogic.security.SSL.trustedCAKeyStorePassPhrase=changeit "); + String managedServerPort; + if (WEBLOGIC_IMAGE_TAG.contains("12")) { + managedServerPort = "8100"; + } else { + managedServerPort = "7002"; + } testUntil( runClientInsidePod(adminServerPodName, domainNamespace, - "/u01", extOpts.toString() + " SslTestClient", "t3s://" - + domainUid + "-cluster-cluster-1:8100"), + "/u01", extOpts.toString() + " SslTestClient", "t3s://" + + domainUid + "-cluster-cluster-1:" + managedServerPort), logger, "Wait for client to get Initial context"); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java index 61abe80772e..51b8614af14 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItOpenshiftIstioMiiDomain.java @@ -227,6 +227,7 @@ void testIstioModelInImageDomain() { templateMap.put("ADMIN_SERVICE",adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); templateMap.put("testwebapp", "sample-war"); + templateMap.put("MANAGED_SERVER_PORT", "7001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java index d7db1e37f85..204e364e41b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItPodTemplates.java @@ -293,6 +293,8 @@ private static void createDomainCrAndVerify(String adminSecretName, .runtimeEncryptionSecret(encryptionSecretName)) .introspectorJobActiveDeadlineSeconds(3000L))); setPodAntiAffinity(domain); + + logger.info(Yaml.dump(domain)); ClusterSpec clusterSpec = new ClusterSpec() .withClusterName(clusterName) @@ -303,6 +305,7 @@ private static void createDomainCrAndVerify(String adminSecretName, ClusterResource cluster = createClusterResource(clusterName, domainNamespace, clusterSpec); logger.info("Creating cluster resource {0} in namespace {1}", clusterName, domainNamespace); + logger.info(Yaml.dump(cluster)); createClusterAndVerify(cluster); // set cluster references domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterName)); diff --git a/integration-tests/src/test/resources/istio/istio-http-template.yaml b/integration-tests/src/test/resources/istio/istio-http-template.yaml index 9ee6eb499da..19622a1891d 100644 --- a/integration-tests/src/test/resources/istio/istio-http-template.yaml +++ b/integration-tests/src/test/resources/istio/istio-http-template.yaml @@ -47,9 +47,9 @@ spec: prefix: /myear - uri: prefix: /testwebapp - - port: 7001 + - port: MANAGED_SERVER_PORT route: - destination: host: CLUSTER_SERVICE port: - number: 7001 + number: MANAGED_SERVER_PORT From 510dce092fa35e1e9d27e9694180fd0d87d697d0 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 18 Mar 2025 13:14:38 -0400 Subject: [PATCH 289/356] Only set container securityContext to a default value if the pod securityContext is also defaulted --- .../operator/helpers/JobStepContext.java | 5 ++++- .../operator/helpers/PodHelper.java | 8 ++++++- .../domain/model/BaseConfiguration.java | 10 +++++++-- .../weblogic/domain/model/DomainV2Test.java | 22 ++++++++++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 355e25f2f1c..587074a2735 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2024, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -513,6 +513,9 @@ V1SecurityContext getInitContainerSecurityContext() { if (isInitDomainOnPVRunAsRoot()) { return new V1SecurityContext().runAsGroup(0L).runAsUser(0L); } + if (getServerSpec().getContainerSecurityContext() != null) { + return getServerSpec().getContainerSecurityContext(); + } if (getPodSecurityContext().equals(PodSecurityHelper.getDefaultPodSecurityContext())) { return PodSecurityHelper.getDefaultContainerSecurityContext(); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java index 1422434b8b6..9234e90ae9e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2023, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -572,6 +572,9 @@ EffectiveServerSpec getServerSpec() { @Override V1SecurityContext getInitContainerSecurityContext() { + if (getServerSpec().getContainerSecurityContext() != null) { + return getServerSpec().getContainerSecurityContext(); + } if (getPodSecurityContext().equals(PodSecurityHelper.getDefaultPodSecurityContext())) { return PodSecurityHelper.getDefaultContainerSecurityContext(); } @@ -858,6 +861,9 @@ protected List getContainerCommand() { @Override V1SecurityContext getInitContainerSecurityContext() { + if (getServerSpec().getContainerSecurityContext() != null) { + return getServerSpec().getContainerSecurityContext(); + } if (getPodSecurityContext().equals(PodSecurityHelper.getDefaultPodSecurityContext())) { return PodSecurityHelper.getDefaultContainerSecurityContext(); } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java index 0168fe1787a..9dee074fd19 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2024, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -298,7 +298,13 @@ void setPodSecurityContext(V1PodSecurityContext podSecurityContext) { } V1SecurityContext getContainerSecurityContext() { - return Optional.ofNullable(serverPod.getContainerSecurityContext()).orElse(getDefaultContainerSecurityContext()); + return Optional.ofNullable(serverPod.getContainerSecurityContext()) + .orElseGet(() -> { + if (serverPod.getPodSecurityContext() == null) { + return getDefaultContainerSecurityContext(); + } + return null; + }); } void setContainerSecurityContext(V1SecurityContext containerSecurityContext) { diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java index 0db0f170821..f0a883981b8 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainV2Test.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2024, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -631,6 +631,26 @@ private void configureDomainWithPodSecurityContext(DomainResource domain) { configureAdminServer().withPodSecurityContext(new V1PodSecurityContext().runAsNonRoot(false)); } + @Test + void whenDefaultContainerSecurityContextConfiguredOnManagedServer() { + V1SecurityContext ms1ContainerSecSpec = + info.getServer(SERVER1, CLUSTER_NAME).getContainerSecurityContext(); + + assertThat(ms1ContainerSecSpec.getRunAsNonRoot(), is(true)); + assertThat(ms1ContainerSecSpec.getPrivileged(), is(false)); + assertThat(ms1ContainerSecSpec.getAllowPrivilegeEscalation(), is(false)); + assertThat(ms1ContainerSecSpec.getCapabilities().getDrop(), contains("ALL")); + } + + @Test + void whenPodSecurityContextConfiguredNoDefaultContainerSecurityContextOnManagedServer() { + configureDomainWithPodSecurityContext(domain); + V1SecurityContext ms1ContainerSecSpec = + info.getServer(SERVER1, CLUSTER_NAME).getContainerSecurityContext(); + + assertThat(ms1ContainerSecSpec, nullValue()); + } + @Test void whenContainerSecurityContextConfiguredOnManagedServerOverrideClusterAndDomain() { configureDomainWithContainerSecurityContext(domain); From e0da97b80873897449d94fe7ae96ea1ae9030841 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 19 Mar 2025 08:51:25 -0400 Subject: [PATCH 290/356] Correct detection of outdated CRD's --- .../kubernetes/operator/helpers/CrdHelper.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java index f6d10720ce1..9be8d15b549 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/CrdHelper.java @@ -772,20 +772,17 @@ private static List getVersions(V1CustomResourceDefinition crd) @Override public boolean isOutdatedCrd(SemanticVersion productVersion, String resourceVersionString, V1CustomResourceDefinition actual, V1CustomResourceDefinition expected) { - ResourceVersion current = new ResourceVersion(resourceVersionString); - List actualVersions = getVersions(actual); - - for (ResourceVersion v : actualVersions) { - if (!isLaterOrEqual(v, current)) { - return false; - } - } - // Check product version label if (productVersion != null) { SemanticVersion currentCrdVersion = KubernetesUtils.getProductVersionFromMetadata(actual.getMetadata()); - if (currentCrdVersion == null || productVersion.compareTo(currentCrdVersion) < 0) { + if (currentCrdVersion == null) { + return false; + } + int compareToResult = productVersion.compareTo(currentCrdVersion); + if (compareToResult < 0) { return false; + } else if (compareToResult > 0) { + return true; } } From efc787c5929c9474e304879d3f6fc81b82d6065f Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Wed, 19 Mar 2025 13:50:38 +0000 Subject: [PATCH 291/356] Correct the behavior when precreateService for server service in cluster... --- .../operator/steps/ManagedServersUpStep.java | 5 +++-- .../domain/model/BaseConfiguration.java | 2 +- .../operator/DomainProcessorTest.java | 18 +++++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/steps/ManagedServersUpStep.java b/operator/src/main/java/oracle/kubernetes/operator/steps/ManagedServersUpStep.java index fa8719d87f8..b0a588a4af4 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/steps/ManagedServersUpStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/steps/ManagedServersUpStep.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2023, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.steps; @@ -105,7 +105,8 @@ private static List getServersToStop( private static boolean isNotAlreadyStoppedOrServiceOnly(DomainPresenceInfo info, ServerShutdownInfo ssi) { return (info.getServerPod(ssi.getServerName()) != null && !info.isServerPodBeingDeleted(ssi.getServerName())) - || (ssi.isServiceOnly() && info.getServerService(ssi.getServerName()) == null); + || (ssi.isServiceOnly() && (info.getServerService(ssi.getServerName()) == null) + || (!ssi.isServiceOnly() && (info.getServerService(ssi.getServerName()) != null))); } private static void insert(List steps, Step step) { diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java index 9dee074fd19..237ca52dd88 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java @@ -363,7 +363,7 @@ public Boolean isPrecreateServerService() { return serverService.isPrecreateService(); } - void setPrecreateServerService(boolean value) { + public void setPrecreateServerService(boolean value) { serverService.setIsPrecreateService(value); } diff --git a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java index 1f6e70efa38..fe98465970d 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2024, Oracle and/or its affiliates. +// Copyright (c) 2019, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -937,6 +937,22 @@ void whenDomainScaledDown_withoutPreCreateServerService_removeService() { assertThat(getServerService(SERVER3).isPresent(), is(false)); } + @Test + void whenDomainWithoutPreCreateServerService_removeService() { + defineServerResources(ADMIN_NAME); + Arrays.stream(MANAGED_SERVER_NAMES).forEach(this::defineServerResources); + domainConfigurator.configureCluster(newInfo, CLUSTER).withReplicas(MIN_REPLICAS).withPrecreateServerService(true); + newInfo.getReferencedClusters().forEach(testSupport::defineResources); + + createMakeRight(newInfo).execute(); + assertThat((int) getServerServices().count(), equalTo(MAX_SERVERS + NUM_ADMIN_SERVERS)); + newInfo.getReferencedClusters().getFirst().getSpec().setPrecreateServerService(false); + newDomain.getSpec().setPrecreateServerService(false); + newDomain.getMetadata().setCreationTimestamp(SystemClock.now()); + processor.createMakeRightOperation(newInfo).withExplicitRecheck().execute(); + assertThat((int) getServerServices().count(), equalTo(MIN_REPLICAS + NUM_ADMIN_SERVERS)); + } + @Test void whenDomainScaledDown_withPreCreateServerService_createClusterIPService() { final String SERVER3 = MANAGED_SERVER_NAMES[2]; From 837117fa32106b9d6d7050b97b6d42574854ec9d Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 19 Mar 2025 15:40:16 -0400 Subject: [PATCH 292/356] Dependency updates --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0f022b5247b..2a8c2f0dd43 100644 --- a/pom.xml +++ b/pom.xml @@ -716,7 +716,7 @@ 4.12.0 3.10.2 1.80 - 5.12.0 + 5.12.1 5.7.1 1.7.0 1.3.2 @@ -732,8 +732,8 @@ 2.12.1 12.1.0 2.0.17 - 1.5.17 - 4.30.0 + 1.5.18 + 4.30.1 2.5.2 10.0.2 ${project.basedir}/src-generated-swagger From 70f5f5c3cf678c9f06ecc190f767e3ea8060a6cb Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Mar 2025 13:09:35 -0400 Subject: [PATCH 293/356] Remove inadvertent change --- .../java/oracle/kubernetes/operator/DomainProcessorTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java index fe98465970d..e79498147a8 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java @@ -521,8 +521,6 @@ void whenDomainChangedSpecAndProcessingAbortedButRestartVersionChanged_runUpdate assertThat(logRecords, not(containsFine(NOT_STARTING_DOMAINUID_THREAD))); } - // HERE - @Test void whenDomainSpecMatchesFailureInfoAndProcessingAborted_dontRunUpdateThread() { DomainResource localDomain = DomainProcessorTestSetup.createTestDomain(); From 804a32cfbf3f098c348c6f10379c35048a845d31 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Mar 2025 13:24:57 -0400 Subject: [PATCH 294/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a8c2f0dd43..8865b62f6ca 100644 --- a/pom.xml +++ b/pom.xml @@ -712,7 +712,7 @@ 4.3.0 19.0.2 3.0.1u2 - 2.1.10 + 2.1.20 4.12.0 3.10.2 1.80 From 8bf8feaf9448ad40cb9053cb15e3dd6a0b9f3470 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 20 Mar 2025 20:55:52 +0000 Subject: [PATCH 295/356] Merge branch 'rmarano-upgrade-webhook' into 'main' Update conversion webhook See merge request weblogic-cloud/weblogic-kubernetes-operator!4916 (cherry picked from commit d147e5f1b2a1b3326d2d84df250ea8352ec28412) d6e02d08 Update conversion webhppk 598e4369 Update example and description 85bd3665 Description improvement e262b25b One more time --- .../managing-operators/conversion-webhook.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/documentation/site/content/managing-operators/conversion-webhook.md b/documentation/site/content/managing-operators/conversion-webhook.md index d904c1ee9c1..8e458c2a7b7 100644 --- a/documentation/site/content/managing-operators/conversion-webhook.md +++ b/documentation/site/content/managing-operators/conversion-webhook.md @@ -83,6 +83,29 @@ The following table describes the behavior of different operator `Helm` chart co **NOTE**: A webhook install is skipped if there's already a webhook deployment at the same or newer version. The `helm install` step requires cluster-level permissions to search for existing conversion webhook deployments in all namespaces. +### Upgrade the conversion webhook + +We support having exactly one installation of the webhook and having one or more installations of the operator. To have more than one installation of the operator, you would need to install a Helm release with just the webhook and then separately install multiple Helm releases with just the operator. The conversion webhook should be updated to at least match the version of the most recent operator in the cluster. + +To upgrade the conversion webhook only, you must have _first_ installed a Helm release with the webhook only, use the `--set webhookOnly=true` option, then you can update that release. + +The following example installs the conversion webhook only (at the specified version), and then upgrades it (to a later, specified version). +``` +kubectl create namespace + +helm repo add weblogic-helm-repository https://oracle.github.io/weblogic-kubernetes-operator/charts --force-update + +helm install weblogic-helm-repository/weblogic-operator --namespace --set webhookOnly=true --version +``` +The first two steps create the namespace and configure Helm with the chart repository. + +The final step installs the webhook specifying that the install should be for the webhook only and at a specific version of the product. + +To upgrade to a later version of the webhook: +``` +helm upgrade weblogic-helm-repository/weblogic-operator --namespace --version +``` + ### Uninstall the conversion webhook {{% notice warning %}} From fd0c06f115c4896db0c6233e0dbb53b391152944 Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Fri, 21 Mar 2025 12:03:46 +0000 Subject: [PATCH 296/356] Support model encryption for initialize domain on PV --- documentation/domains/Domain.json | 4 + documentation/domains/Domain.md | 1 + .../managing-domains/domain-on-pv/usage.md | 16 +++ .../weblogic/domain/InitializeDomainOnPV.java | 23 +++- .../kubernetes/ItSystemResOverrides.java | 123 +++++++++++++++++- kubernetes/crd/domain-crd.yaml | 10 +- .../operator/helpers/JobStepContext.java | 28 +++- .../helpers/StepContextConstants.java | 4 +- .../weblogic/domain/model/DomainResource.java | 10 +- .../weblogic/domain/model/DomainSpec.java | 8 +- .../domain/model/InitializeDomainOnPV.java | 29 ++++- .../resources/scripts/createDomainOnPV.sh | 15 ++- .../helpers/DomainIntrospectorJobTest.java | 20 +++ .../weblogic/domain/DomainConfigurator.java | 9 ++ .../model/DomainCommonConfigurator.java | 13 ++ 15 files changed, 293 insertions(+), 20 deletions(-) diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index ebf49cd8c73..3c5eec032ee 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -743,6 +743,10 @@ "description": "An optional field that describes the configuration for creating a PersistentVolumeClaim for `Domain on PV`. PersistentVolumeClaim is a user\u0027s request for and claim to a persistent volume. The operator will perform this one-time create operation only if the persistent volume claim does not already exist. Omit this section if you have manually created a persistent volume claim. If specified, the name must match one of the volumes under `serverPod.volumes` and the domain home must reside in the mount path of the volume using this claim. More info: https://oracle.github.io/weblogic-kubernetes-operator/managing-domains/domain-on-pv-initialization#pvc", "$ref": "#/definitions/PersistentVolumeClaim" }, + "modelEncryptionPassphraseSecret": { + "description": "Specifies the secret name of the WebLogic Deployment Tool encryption passphrase if the WDT models provided in the \u0027domainCreationImages\u0027 or \u0027domainCreationConfigMap\u0027 are encrypted using the WebLogic Deployment Tool \u0027encryptModel\u0027 command. The secret must use the key \u0027passphrase\u0027 containing the actual passphrase for encryption.", + "type": "string" + }, "setDefaultSecurityContextFsGroup": { "description": "Specifies whether the operator will set the default \u0027fsGroup\u0027 in the introspector job pod security context. This is needed to create the domain home directory on PV in some environments. If the \u0027fsGroup\u0027 is specified as part of \u0027spec.introspector.serverPod.podSecurityContext\u0027, then the operator will use that \u0027fsGroup\u0027 instead of the default \u0027fsGroup\u0027. Defaults to true.", "type": "boolean" diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 77b4de1cb75..1a8130ce2e1 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -244,6 +244,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | Name | Type | Description | | --- | --- | --- | | `domain` | [Domain On PV](#domain-on-pv) | Describes the configuration for creating an initial WebLogic Domain in persistent volume (`Domain in PV`). The operator will not recreate or update the domain if it already exists. Required. | +| `modelEncryptionPassphraseSecret` | string | Specifies the secret name of the WebLogic Deployment Tool encryption passphrase if the WDT models provided in the 'domainCreationImages' or 'domainCreationConfigMap' are encrypted using the WebLogic Deployment Tool 'encryptModel' command. The secret must use the key 'passphrase' containing the actual passphrase for encryption. | | `persistentVolume` | [Persistent Volume](#persistent-volume) | An optional field that describes the configuration to create a PersistentVolume for `Domain on PV` domain. Omit this section if you have manually created a persistent volume. The operator will perform this one-time create operation only if the persistent volume does not already exist. The operator will not recreate or update the PersistentVolume when it exists. More info: https://oracle.github.io/weblogic-kubernetes-operator/managing-domains/domain-on-pv-initialization#pv | | `persistentVolumeClaim` | [Persistent Volume Claim](#persistent-volume-claim) | An optional field that describes the configuration for creating a PersistentVolumeClaim for `Domain on PV`. PersistentVolumeClaim is a user's request for and claim to a persistent volume. The operator will perform this one-time create operation only if the persistent volume claim does not already exist. Omit this section if you have manually created a persistent volume claim. If specified, the name must match one of the volumes under `serverPod.volumes` and the domain home must reside in the mount path of the volume using this claim. More info: https://oracle.github.io/weblogic-kubernetes-operator/managing-domains/domain-on-pv-initialization#pvc | | `runDomainInitContainerAsRoot` | Boolean | Specifies whether the operator will run the domain initialization init container in the introspector job as root. This may be needed in some environments to create the domain home directory on PV. Defaults to false. | diff --git a/documentation/site/content/managing-domains/domain-on-pv/usage.md b/documentation/site/content/managing-domains/domain-on-pv/usage.md index 409c5574241..d7a4bc71166 100644 --- a/documentation/site/content/managing-domains/domain-on-pv/usage.md +++ b/documentation/site/content/managing-domains/domain-on-pv/usage.md @@ -29,6 +29,7 @@ To use this feature, provide the following information: - [Domain information](#domain-information) - This describes the domain type and whether the operator should create the RCU schema. - [Domain WDT models](#domain-creation-models) - This is where the WDT Home, WDT model, WDT archive, and WDT variables files reside. - [Optional WDT models ConfigMap](#optional-wdt-models-configmap) - Optional, WDT model, WDT variables files. +- [Using WDT model encryption](#using-wdt-model-encryption) - Optional, using WDT model encryption. - [Domain resource YAML file]({{< relref "/reference/domain-resource.md">}}) - This is for deploying the domain in WebLogic Kubernetes Operator. @@ -113,6 +114,21 @@ those in `domainCreationImages`. The files inside this ConfigMap must have file extensions, `.yaml`, `.properties`, or `.zip`. +#### Using WDT model encryption + +Starting in WebLogic Kubernetes Operator version 4.2.16. If the provided WDT models are encrypted using the WDT `encryptModel` +command. You can specify the encryption passphrase as a secret in the domain resource YAML. WDT will use the value in the +secret to decrypt the models for domain creation. + +```yaml + initializeDomainOnPV: + modelEncryptionPassphraseSecret: model-encryption-secret +``` + +The secret must have a key `passphrase` containing the value of the WDT encryption passphrase used to encrypt the models. + +`kubectl create secret generic model-encrypion-secret --from-literal=passphrase=` + #### Volumes and VolumeMounts information You must provide the `volumes` and `volumeMounts` information in `domain.spec.serverPod`. This allows the pod to mount the persistent diff --git a/integration-tests/src/test/java/oracle/weblogic/domain/InitializeDomainOnPV.java b/integration-tests/src/test/java/oracle/weblogic/domain/InitializeDomainOnPV.java index 80ec0d0f72d..2caeab83098 100644 --- a/integration-tests/src/test/java/oracle/weblogic/domain/InitializeDomainOnPV.java +++ b/integration-tests/src/test/java/oracle/weblogic/domain/InitializeDomainOnPV.java @@ -51,6 +51,13 @@ public class InitializeDomainOnPV { + " If the 'fsGroup' is specified as part of 'spec.introspector.serverPod.podSecurityContext', then the operator" + " will use that 'fsGroup' instead of the default 'fsGroup'. Defaults to true.") public Boolean setDefaultSecurityContextFsGroup; + + @ApiModelProperty("Specifies the secret name of the WebLogic Deployment Tool encryption passphrase if the WDT models " + + "provided in the 'domainCreationImages' or 'domainCreationConfigMap' are encrypted using the " + + "WebLogic Deployment Tool 'encryptModel' command. " + + "The secret must use the key 'passphrase' containing the actual passphrase for encryption.") + public String modelEncryptionPassphraseSecret; + public PersistentVolume getPersistentVolume() { return persistentVolume; @@ -105,6 +112,15 @@ public InitializeDomainOnPV setDefaultFsGroup(Boolean setDefaultFsGroup) { this.setDefaultSecurityContextFsGroup = setDefaultFsGroup; return this; } + + public String getModelEncryptionPassphraseSecret() { + return modelEncryptionPassphraseSecret; + } + + public InitializeDomainOnPV modelEncryptionPassphraseSecret(String modelEncryptionPassphraseSecret) { + this.modelEncryptionPassphraseSecret = modelEncryptionPassphraseSecret; + return this; + } @Override public String toString() { @@ -138,12 +154,13 @@ public boolean equals(Object other) { } InitializeDomainOnPV rhs = ((InitializeDomainOnPV) other); - EqualsBuilder builder = - new EqualsBuilder() + EqualsBuilder builder + = new EqualsBuilder() .append(persistentVolume, rhs.persistentVolume) .append(persistentVolumeClaim, rhs.persistentVolumeClaim) .append(domain, rhs.domain) - .append(waitForPvcToBind, rhs.waitForPvcToBind); + .append(waitForPvcToBind, rhs.waitForPvcToBind) + .append(modelEncryptionPassphraseSecret, rhs.modelEncryptionPassphraseSecret); return builder.isEquals(); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index 7c3ddf713e0..a3a53cee942 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -4,8 +4,11 @@ package oracle.weblogic.kubernetes; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.OffsetDateTime; @@ -15,7 +18,9 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.concurrent.Callable; +import java.util.stream.Collectors; import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.V1Patch; @@ -24,6 +29,7 @@ import io.kubernetes.client.openapi.models.V1LocalObjectReference; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; +import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Volume; import io.kubernetes.client.openapi.models.V1VolumeMount; import oracle.weblogic.domain.AdminServer; @@ -38,6 +44,8 @@ import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.Command; +import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; import oracle.weblogic.kubernetes.actions.impl.primitive.WitParams; import oracle.weblogic.kubernetes.annotations.IntegrationTest; import oracle.weblogic.kubernetes.annotations.Namespaces; @@ -59,12 +67,17 @@ import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.DOWNLOAD_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; +import static oracle.weblogic.kubernetes.actions.TestActions.createSecret; import static oracle.weblogic.kubernetes.actions.TestActions.getNextIntrospectVersion; import static oracle.weblogic.kubernetes.actions.TestActions.getServiceNodePort; import static oracle.weblogic.kubernetes.actions.TestActions.getServicePort; @@ -72,6 +85,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.startDomain; import static oracle.weblogic.kubernetes.actions.impl.Domain.patchDomainCustomResource; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podStateNotChanged; +import static oracle.weblogic.kubernetes.assertions.TestAssertions.secretExists; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createAndPushAuxiliaryImage; import static oracle.weblogic.kubernetes.utils.BuildApplication.buildApplication; @@ -91,7 +105,6 @@ import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapFromFiles; import static oracle.weblogic.kubernetes.utils.DeployUtil.deployUsingWlst; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; -import static oracle.weblogic.kubernetes.utils.FileUtils.createWdtPropertyFile; import static oracle.weblogic.kubernetes.utils.FmwUtils.getConfiguration; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; @@ -152,6 +165,10 @@ class ItSystemResOverrides { static Path sitconfigAppPath; String overridecm = "configoverride-cm"; LinkedHashMap podTimestamps; + + private static Path encryptModelScript; + private static final String passPhrase = "encryptPA55word"; + private static final String encryptionSecret = "model-encryption-secret"; private static LoggingFacade logger = null; @@ -166,7 +183,7 @@ class ItSystemResOverrides { * @param namespaces injected by JUnit */ @BeforeAll - public void initAll(@Namespaces(2) List namespaces) { + public void initAll(@Namespaces(2) List namespaces) throws IOException { logger = getLogger(); logger.info("Assign a unique namespace for operator"); @@ -175,6 +192,9 @@ public void initAll(@Namespaces(2) List namespaces) { logger.info("Assign a unique namespace for domain namspace"); assertNotNull(namespaces.get(1), "Namespace is null"); domainNamespace = namespaces.get(1); + + logger.info("installing WebLogic Deploy Tool"); + downloadAndInstallWDT(); // install operator and verify its running in ready state installAndVerifyOperator(opNamespace, domainNamespace); @@ -391,7 +411,7 @@ private void verifyIntrospectorRuns() { } //create a standard WebLogic domain. - private void createDomain() { + private void createDomain() throws IOException { String uniqueDomainHome = "/shared/" + domainNamespace + "/domains/"; // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster @@ -404,8 +424,17 @@ private void createDomain() { final String wlsModelFilePrefix = "sitconfig-dci-model"; final String wlsModelFile = wlsModelFilePrefix + ".yaml"; t3ChannelPort = getNextFreePort(); + logger.info("Create WDT property file"); File wlsModelPropFile = createWdtPropertyFile(wlsModelFilePrefix, K8S_NODEPORT_HOST, t3ChannelPort); + logger.info("Create WDT passphrase file"); + File passphraseFile = createPassphraseFile(passPhrase); + logger.info("Run encruptModel.sh script to encrypt clear text password in property file"); + encryptModel(encryptModelScript, + Path.of(MODEL_DIR, wlsModelFile), + wlsModelPropFile.toPath(), passphraseFile.toPath()); + createSecretWithUsernamePassword(wlSecretName, opNamespace, clusterName, passPhrase); + createEncryptionSecret(encryptionSecret, domainNamespace); // create domainCreationImage String domainCreationImageName = DOMAIN_IMAGES_PREFIX + "wls-domain-on-pv-image"; @@ -437,10 +466,12 @@ private void createDomain() { configuration = getConfiguration(pvName, pvcName, pvCapacity, pvcRequest, storageClassName, ItSystemResOverrides.class.getSimpleName()); } - configuration.getInitializeDomainOnPV().domain(new DomainOnPV() - .createMode(CreateIfNotExists.DOMAIN) - .domainCreationImages(Collections.singletonList(domainCreationImage)) - .domainType(DomainOnPVType.WLS)); + configuration.getInitializeDomainOnPV() + .modelEncryptionPassphraseSecret(encryptionSecret) + .domain(new DomainOnPV() + .createMode(CreateIfNotExists.DOMAIN) + .domainCreationImages(Collections.singletonList(domainCreationImage)) + .domainType(DomainOnPVType.WLS)); configuration.overrideDistributionStrategy("Dynamic"); // create secrets @@ -628,4 +659,82 @@ private void restartDomain() { checkPodReadyAndServiceExists(managedServerPodNamePrefix + i, domainUid, domainNamespace); } } + + public static File createWdtPropertyFile(String wlsModelFilePrefix, String nodePortHost, int t3Port) { + // create property file used with domain model file + Properties p = new Properties(); + p.setProperty("WebLogicAdminUserName", ADMIN_USERNAME_DEFAULT); + p.setProperty("WebLogicAdminPassword", ADMIN_PASSWORD_DEFAULT); + p.setProperty("K8S_NODEPORT_HOST", nodePortHost); + p.setProperty("T3_CHANNEL_PORT", Integer.toString(t3Port)); + + // create a model property file + File domainPropertiesFile = assertDoesNotThrow(() -> + File.createTempFile(wlsModelFilePrefix, ".properties", new File(RESULTS_TEMPFILE)), + "Failed to create WLS model properties file"); + + // create the property file + assertDoesNotThrow(() -> + p.store(new FileOutputStream(domainPropertiesFile), "WLS properties file"), + "Failed to write WLS properties file"); + + return domainPropertiesFile; + } + + private static void downloadAndInstallWDT() throws IOException { + String wdtUrl = WDT_DOWNLOAD_URL + "/download/weblogic-deploy.zip"; + Path destLocation = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy.zip"); + encryptModelScript = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy", "bin", "encryptModel.sh"); + if (!Files.exists(destLocation) && !Files.exists(encryptModelScript)) { + logger.info("Downloading WDT to {0}", destLocation); + Files.createDirectories(destLocation.getParent()); + OracleHttpClient.downloadFile(wdtUrl, destLocation.toString(), null, null, 3); + String cmd = "cd " + destLocation.getParent() + ";unzip " + destLocation; + assertTrue(Command.withParams(new CommandParams().command(cmd)).execute(), "unzip command failed"); + } + assertTrue(Files.exists(encryptModelScript), "could not find createDomain.sh script"); + } + + private static File createPassphraseFile(String passPhrase) throws IOException { + // create pass phrase file used with domain model file + File passphraseFile = assertDoesNotThrow(() + -> File.createTempFile("passphrase", ".txt", new File(RESULTS_TEMPFILE)), + "Failed to create WLS model encrypt passphrase file"); + Files.write(passphraseFile.toPath(), passPhrase.getBytes(StandardCharsets.UTF_8)); + logger.info("passphrase file contents {0}", Files.readString(passphraseFile.toPath())); + return passphraseFile; + } + + private static void encryptModel(Path encryptModelScript, Path modelFile, + Path propertyFile, Path passphraseFile) throws IOException { + Path mwHome = Path.of(RESULTS_ROOT, "mwhome"); + logger.info("Encrypting property file containing the secrets {0}", propertyFile.toString()); + List command = List.of( + encryptModelScript.toString(), + "-oracle_home", mwHome.toString(), + "-model_file", modelFile.toString(), + "-variable_file", propertyFile.toString(), + "-passphrase_file", passphraseFile.toString() + ); + logger.info("running {0}", command); + assertTrue(Command.withParams(new CommandParams() + .command(command.stream().collect(Collectors.joining(" ")))).execute(), + "encryptModel.sh command failed"); + logger.info("Encrypted passphrase file contents {0}", Files.readString(propertyFile)); + } + + public static void createEncryptionSecret(String secretName, String namespace) { + Map secretMap = new HashMap<>(); + secretMap.put("passphrase", passPhrase); + + if (!secretExists(secretName, namespace)) { + boolean secretCreated = assertDoesNotThrow(() -> createSecret(new V1Secret() + .metadata(new V1ObjectMeta() + .name(secretName) + .namespace(namespace)) + .stringData(secretMap)), "Create secret failed with ApiException"); + + assertTrue(secretCreated, String.format("create secret failed for %s", secretName)); + } + } } diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index 88e1655a5e1..c0963bfc08a 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: deb0c93b1b3e11669640a91830463ef0e6c8a5288a2d1c902e6f8a3c42d421b0 + weblogic.sha256: 3c9cb1077e51677d24c80535ad61c7b524c415e44dda7eda48e9e344f4bb3809 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -501,6 +501,14 @@ spec: type: object type: object type: object + modelEncryptionPassphraseSecret: + description: Specifies the secret name of the WebLogic Deployment + Tool encryption passphrase if the WDT models provided in + the 'domainCreationImages' or 'domainCreationConfigMap' + are encrypted using the WebLogic Deployment Tool 'encryptModel' + command. The secret must use the key 'passphrase' containing + the actual passphrase for encryption. + type: string setDefaultSecurityContextFsGroup: description: Specifies whether the operator will set the default 'fsGroup' in the introspector job pod security context. diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 587074a2735..68cdef91df6 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -238,6 +238,9 @@ String getRuntimeEncryptionSecretName() { return getDomain().getRuntimeEncryptionSecret(); } + String getModelEncryptionSecretName() { + return getDomain().getModelEncryptionSecret(); + } // ----------------------- step methods ------------------------------ @@ -602,11 +605,18 @@ protected V1PodSpec createPodSpec() { podSpec.addVolumesItem(new V1Volume().name(OPSS_KEYPASSPHRASE_VOLUME).secret( getOpssWalletPasswordSecretVolume())); } + if (getOpssWalletFileSecretName() != null) { podSpec.addVolumesItem(new V1Volume().name(OPSS_WALLETFILE_VOLUME).secret( getOpssWalletFileSecretVolume())); } + if (getModelEncryptionSecretVolume() != null) { + podSpec.addVolumesItem(new V1Volume().name(WDT_MODEL_ENCRYPTION_PASSPHRASE_VOLUME).secret( + getModelEncryptionSecretVolume() + )); + } + podSpec.setImagePullSecrets(info.getDomain().getSpec().getImagePullSecrets()); for (V1Volume additionalVolume : getAdditionalVolumes()) { @@ -729,7 +739,12 @@ protected V1Container createPrimaryContainer() { if (getOpssWalletFileSecretVolume() != null) { container.addVolumeMountsItem(readOnlyVolumeMount(OPSS_WALLETFILE_VOLUME, OPSS_WALLETFILE_MOUNT_PATH)); } - + + if (getModelEncryptionSecretVolume() != null) { + container.addVolumeMountsItem(readOnlyVolumeMount(WDT_MODEL_ENCRYPTION_PASSPHRASE_VOLUME, + WDT_MODEL_ENCRYPTION_PASSPHRASE_MOUNT_PATH)); + } + for (V1VolumeMount additionalVolumeMount : getAdditionalVolumeMounts()) { container.addVolumeMountsItem(additionalVolumeMount); } @@ -885,6 +900,17 @@ private V1SecretVolumeSource getOpssWalletFileSecretVolume() { return null; } + private V1SecretVolumeSource getModelEncryptionSecretVolume() { + if (getModelEncryptionSecretName() != null) { + V1SecretVolumeSource result = new V1SecretVolumeSource() + .secretName(getModelEncryptionSecretName()) + .defaultMode(420); + result.setOptional(true); + return result; + } + return null; + } + private V1ConfigMapVolumeSource getConfigMapVolumeSource() { return new V1ConfigMapVolumeSource() .name(KubernetesConstants.SCRIPT_CONFIG_MAP_NAME) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java index 28648ae9ecc..f36619c5f2d 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2024, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -11,6 +11,7 @@ public interface StepContextConstants { String DEBUG_CM_VOLUME = "weblogic-domain-debug-cm-volume"; String INTROSPECTOR_VOLUME = "weblogic-domain-introspect-cm-volume"; String RUNTIME_ENCRYPTION_SECRET_VOLUME = "weblogic-domain-runtime-encryption-volume"; + String WDT_MODEL_ENCRYPTION_PASSPHRASE_VOLUME = "wdt-encryption-passphrase-volume"; String FLUENTD_CONFIGMAP_VOLUME = "weblogic-fluentd-configmap-volume"; String OLD_FLUENTD_CONFIGMAP_NAME = "weblogic-fluentd-configmap"; String FLUENTD_CONFIGMAP_NAME_SUFFIX = "-" + OLD_FLUENTD_CONFIGMAP_NAME; @@ -23,6 +24,7 @@ public interface StepContextConstants { String FLUENTBIT_CONFIGMAP_NAME_SUFFIX = "-" + "weblogic-fluentbit-configmap"; String SECRETS_MOUNT_PATH = "/weblogic-operator/secrets"; String OPSS_KEY_MOUNT_PATH = "/weblogic-operator/opss-walletkey-secret"; + String WDT_MODEL_ENCRYPTION_PASSPHRASE_MOUNT_PATH = "/weblogic-operator/wdt-encryption-passphrase"; String RUNTIME_ENCRYPTION_SECRET_MOUNT_PATH = "/weblogic-operator/model-runtime-secret"; String OVERRIDE_SECRETS_MOUNT_PATH = "/weblogic-operator/config-overrides-secrets"; String OVERRIDES_CM_MOUNT_PATH = "/weblogic-operator/config-overrides"; diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java index f2ddac634d2..18174f602e4 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainResource.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2024, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -447,6 +447,14 @@ public String getModelOpssWalletPasswordSecret() { return spec.getModelOpssWalletPasswordSecret(); } + /** + * Reference to secret name of the wdt encryption passphrase for domain on pv. + * @return wdt model encryption passphrase secret name + */ + public String getModelEncryptionSecret() { + return spec.getModelEncryptionSecret(); + } + /** * Reference to runtime encryption secret. * diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainSpec.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainSpec.java index 29186cab31b..673ba043ab9 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainSpec.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/DomainSpec.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2024, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -982,6 +982,12 @@ private String getModelOpssWalletFileSecret() { .orElse(null); } + String getModelEncryptionSecret() { + return Optional.ofNullable(getInitializeDomainOnPV()) + .map(InitializeDomainOnPV::getModelEncryptionPassphraseSecret) + .orElse(null); + } + private String getInitializeDomainOnPVOpssWalletFileSecret() { return Optional.ofNullable(getInitializeDomainOnPV()) .map(InitializeDomainOnPV::getDomain) diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/InitializeDomainOnPV.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/InitializeDomainOnPV.java index 91aedf7ac25..b0ff8490305 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/InitializeDomainOnPV.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/InitializeDomainOnPV.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -52,6 +52,12 @@ public class InitializeDomainOnPV { + " will use that 'fsGroup' instead of the default 'fsGroup'. Defaults to true.") Boolean setDefaultSecurityContextFsGroup; + @Description("Specifies the secret name of the WebLogic Deployment Tool encryption passphrase if the WDT models " + + "provided in the 'domainCreationImages' or 'domainCreationConfigMap' are encrypted using the " + + "WebLogic Deployment Tool 'encryptModel' command. " + + "The secret must use the key 'passphrase' containing the actual passphrase for encryption.") + String modelEncryptionPassphraseSecret; + public PersistentVolume getPersistentVolume() { return persistentVolume; } @@ -106,6 +112,15 @@ public InitializeDomainOnPV setDefaultFsGroup(Boolean setDefaultFsGroup) { return this; } + public String getModelEncryptionPassphraseSecret() { + return modelEncryptionPassphraseSecret; + } + + public InitializeDomainOnPV modelEncryptionPassphraseSecret(String modelEncryptionPassphraseSecret) { + this.modelEncryptionPassphraseSecret = modelEncryptionPassphraseSecret; + return this; + } + @Override public String toString() { ToStringBuilder builder = @@ -113,7 +128,9 @@ public String toString() { .append("persistentVolume", persistentVolume) .append("persistentVolumeClaim", persistentVolumeClaim) .append("domain", domain) - .append("waitForPvcToBind", waitForPvcToBind); + .append("waitForPvcToBind", waitForPvcToBind) + .append("modelEncryptionPassphraseSecret", modelEncryptionPassphraseSecret) + .append("runDomainInitContainerAsRoot", runDomainInitContainerAsRoot); return builder.toString(); } @@ -124,7 +141,9 @@ public int hashCode() { .append(persistentVolume) .append(persistentVolumeClaim) .append(domain) - .append(waitForPvcToBind); + .append(waitForPvcToBind) + .append(modelEncryptionPassphraseSecret) + .append(runDomainInitContainerAsRoot); return builder.toHashCode(); } @@ -143,7 +162,9 @@ public boolean equals(Object other) { .append(persistentVolume, rhs.persistentVolume) .append(persistentVolumeClaim, rhs.persistentVolumeClaim) .append(domain, rhs.domain) - .append(waitForPvcToBind, rhs.waitForPvcToBind); + .append(waitForPvcToBind, rhs.waitForPvcToBind) + .append(runDomainInitContainerAsRoot, rhs.runDomainInitContainerAsRoot) + .append(modelEncryptionPassphraseSecret, rhs.modelEncryptionPassphraseSecret); return builder.isEquals(); } diff --git a/operator/src/main/resources/scripts/createDomainOnPV.sh b/operator/src/main/resources/scripts/createDomainOnPV.sh index 45fb5619e76..b7fd8f6be7f 100644 --- a/operator/src/main/resources/scripts/createDomainOnPV.sh +++ b/operator/src/main/resources/scripts/createDomainOnPV.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2023, 2024, Oracle and/or its affiliates. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # # This script contains the all the function of creating domain on pv @@ -12,6 +12,9 @@ source ${SCRIPTPATH}/wdt_common.sh # we export the opss password file location because it's also used by introspectDomain.py export OPSS_KEY_PASSPHRASE="/weblogic-operator/opss-walletkey-secret/walletPassword" OPSS_KEY_B64EWALLET="/weblogic-operator/opss-walletfile-secret/walletFile" +WDT_MODEL_ENCRYPTION_PASSPHRASE_ROOT="/weblogic-operator/wdt-encryption-passphrase" +WDT_MODEL_ENCRYPTION_PASSPHRASE="${WDT_MODEL_ENCRYPTION_PASSPHRASE_ROOT}/passphrase" + IMG_MODELS_HOME="/auxiliary/models" IMG_MODELS_ROOTDIR="${IMG_MODELS_HOME}" IMG_ARCHIVES_ROOTDIR="${IMG_MODELS_HOME}" @@ -210,6 +213,16 @@ createDomainFromWDTModel() { cp /weblogic-operator/scripts/dopv-filters.json "${WDT_CUSTOM_CONFIG}/model_filters.json" || exitOrLoop fi + if [ -d "${WDT_MODEL_ENCRYPTION_PASSPHRASE_ROOT}" ]; then + if [ ! -f "${WDT_MODEL_ENCRYPTION_PASSPHRASE}" ]; then + trace SEVERE "Domain Source Type is 'DomainOnPV' and you have specified " \ + " 'initializeDomainOnPV.modelEncryptionPassphraseSecret' but this secret does not have the required key " \ + " 'passphrase', update the secret and rerun the introspector job." + exitOrLoop + fi + wdtArgs+=" -passphrase_file ${WDT_MODEL_ENCRYPTION_PASSPHRASE}" + fi + if [ -z "${OPSS_FLAGS}" ]; then # Determine run rcu or not diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java index 0758bb8da0a..4a95b3399ad 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/DomainIntrospectorJobTest.java @@ -137,6 +137,8 @@ import static oracle.kubernetes.operator.helpers.StepContextConstants.OPSS_WALLETFILE_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.SECRETS_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.WDTCONFIGMAP_MOUNT_PATH; +import static oracle.kubernetes.operator.helpers.StepContextConstants.WDT_MODEL_ENCRYPTION_PASSPHRASE_MOUNT_PATH; +import static oracle.kubernetes.operator.helpers.StepContextConstants.WDT_MODEL_ENCRYPTION_PASSPHRASE_VOLUME; import static oracle.kubernetes.operator.tuning.TuningParameters.DOMAIN_PRESENCE_RECHECK_INTERVAL_SECONDS; import static oracle.kubernetes.weblogic.domain.model.AuxiliaryImage.AUXILIARY_IMAGE_DEFAULT_SOURCE_WDT_INSTALL_HOME; import static oracle.kubernetes.weblogic.domain.model.AuxiliaryImage.AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME; @@ -505,6 +507,24 @@ void whenJobCreatedWithInitializeDomainOnPVDefined_hasSecretsVolumeAndMounts() { .mountPath(OPSS_KEY_MOUNT_PATH).readOnly(true))); } + @Test + void whenJobCreatedWithInitDomainOnPVWithModelEncryption_hasSecretsVolumeAndMounts() { + getConfigurator().withInitializeDomainOnPVModelEncryptionSecret("encryptedSecret"); + testSupport.defineResources(createSecret("encryptedSecret")); + + List jobs = runStepsAndGetJobs(); + V1Job job = jobs.get(0); + + assertThat(getJobPodSpec(job).getVolumes(), + hasItem(new V1Volume().name(WDT_MODEL_ENCRYPTION_PASSPHRASE_VOLUME).secret( + new V1SecretVolumeSource().secretName("encryptedSecret").optional(true).defaultMode(420)))); + assertThat(getCreatedPodSpecContainers(jobs).get(0).getVolumeMounts(), + hasItem(new V1VolumeMount().name(WDT_MODEL_ENCRYPTION_PASSPHRASE_VOLUME) + .mountPath(WDT_MODEL_ENCRYPTION_PASSPHRASE_MOUNT_PATH).readOnly(true))); + + } + + private V1Secret createSecret(String name) { return new V1Secret().metadata(new V1ObjectMeta().name(name).namespace(NS)); } diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/DomainConfigurator.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/DomainConfigurator.java index 1e33a6d4a31..0f1a75fdea8 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/DomainConfigurator.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/DomainConfigurator.java @@ -725,5 +725,14 @@ public abstract DomainConfigurator withConfigurationForInitializeDomainOnPV( */ public abstract DomainConfigurator withDomainCreationConfigMap(String cm); + /** + * Add model encryption secret for the domain resource's initializeDomainOnPV. + * + * @param secret the model encryption file secret + * @return this object + */ + public abstract DomainConfigurator withInitializeDomainOnPVModelEncryptionSecret(String secret); + + public abstract DomainConfigurator withModel(Model model); } diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java index 64d9e63d5a8..33110cbcd40 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/DomainCommonConfigurator.java @@ -476,6 +476,12 @@ public DomainConfigurator withInitializeDomainOnPVOpssWalletFileSecret(String se return this; } + @Override + public DomainConfigurator withInitializeDomainOnPVModelEncryptionSecret(String secret) { + getOrCreateInitializeDomainOnPVModelSecret(secret); + return this; + } + @Override public DomainConfigurator withInitializeDomainOnPVType(String type) { getOrCreateInitializeDomainOnPVDomain().domainType(type); @@ -570,6 +576,13 @@ private Opss getOrCreateInitializeDomainOnPVOpss() { return domain.getOpss(); } + private void getOrCreateInitializeDomainOnPVModelSecret(String secretName) { + InitializeDomainOnPV initializeDomainOnPV = getOrCreateInitializeDomainOnPV(); + if (initializeDomainOnPV.getModelEncryptionPassphraseSecret() == null) { + initializeDomainOnPV.modelEncryptionPassphraseSecret(secretName); + } + } + @Override public void setShuttingDown(boolean shuttingDown) { configureAdminServer().withServerStartPolicy(shuttingDown ? ServerStartPolicy.NEVER : ServerStartPolicy.ALWAYS); From 2e987d653809187355eb8c401106905480588c31 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 24 Mar 2025 13:16:02 +0000 Subject: [PATCH 297/356] Update MonitoringExporterFiltering tests to adjust changes for wls-exporter servlet package name changes --- Jenkinsfile | 4 +- Jenkinsfile.armoke | 4 +- Jenkinsfile.kindnightly | 4 +- Jenkinsfile.ocne | 4 +- Jenkinsfile.ocne19 | 4 +- Jenkinsfile.oke | 4 +- Jenkinsfile.podman | 4 +- Jenkinsfile.podman.upgrade | 4 +- .../ItMonitoringExporterMetricsFiltering.java | 122 ++++-- .../weblogic/kubernetes/TestConstants.java | 4 +- .../kubernetes/utils/MonitoringUtils.java | 365 +++++++++++------- 11 files changed, 332 insertions(+), 191 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index fe8f7a42db3..9bee0ec97de 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2024, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // def kind_k8s_map = [ @@ -168,7 +168,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.2' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.armoke b/Jenkinsfile.armoke index 3856f2c6ebe..99141ada1e8 100644 --- a/Jenkinsfile.armoke +++ b/Jenkinsfile.armoke @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // @@ -179,7 +179,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.3' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.kindnightly b/Jenkinsfile.kindnightly index 6ddc8737ac4..7cfd35e1ee0 100644 --- a/Jenkinsfile.kindnightly +++ b/Jenkinsfile.kindnightly @@ -1,4 +1,4 @@ -// Copyright (c) 2022, 2024, Oracle and/or its affiliates. +// Copyright (c) 2022, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // def kind_k8s_map = [ @@ -185,7 +185,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.2' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.ocne b/Jenkinsfile.ocne index 5c9e7fc6bcc..ed256c56027 100644 --- a/Jenkinsfile.ocne +++ b/Jenkinsfile.ocne @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // @@ -176,7 +176,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.3' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.ocne19 b/Jenkinsfile.ocne19 index 47168819138..145b10f6bba 100644 --- a/Jenkinsfile.ocne19 +++ b/Jenkinsfile.ocne19 @@ -1,4 +1,4 @@ -// Copyright (c) 2024, Oracle and/or its affiliates. +// Copyright (c) 2024, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // @@ -167,7 +167,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.3' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index bd7fb36b961..948728c1482 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -1,4 +1,4 @@ -// Copyright (c) 2023, 2024, Oracle and/or its affiliates. +// Copyright (c) 2023, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // @@ -193,7 +193,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.2.2' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.podman b/Jenkinsfile.podman index 891182a7ba7..de4a1bd905d 100644 --- a/Jenkinsfile.podman +++ b/Jenkinsfile.podman @@ -1,4 +1,4 @@ -// Copyright (c) 2024, Oracle and/or its affiliates. +// Copyright (c) 2024, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // def kind_k8s_map = [ @@ -178,7 +178,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.2' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/Jenkinsfile.podman.upgrade b/Jenkinsfile.podman.upgrade index e03f72987e5..3cb22128cc7 100644 --- a/Jenkinsfile.podman.upgrade +++ b/Jenkinsfile.podman.upgrade @@ -1,4 +1,4 @@ -// Copyright (c) 2024, Oracle and/or its affiliates. +// Copyright (c) 2024, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // def kind_k8s_map = [ @@ -185,7 +185,7 @@ pipeline { ) string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', description: '', - defaultValue: '2.1.2' + defaultValue: '2.3.0' ) string(name: 'PROMETHEUS_CHART_VERSION', description: '', diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java index 5e53a6e18ca..e4f93da0df7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterMetricsFiltering.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, 2024, Oracle and/or its affiliates. +// Copyright (c) 2023, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -44,12 +44,14 @@ import static oracle.weblogic.kubernetes.TestConstants.IT_MONITORINGEXPORTERMF_PROMETHEUS_HTTP_NODEPORT; import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; +import static oracle.weblogic.kubernetes.TestConstants.MONITORING_EXPORTER_WEBAPP_VERSION; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER_PRIVATEIP; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_CHART_VERSION; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTPS_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; @@ -72,6 +74,7 @@ import static oracle.weblogic.kubernetes.utils.MonitoringUtils.installAndVerifyGrafana; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.installAndVerifyPrometheus; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.installMonitoringExporter; +import static oracle.weblogic.kubernetes.utils.MonitoringUtils.replaceValueInFile; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.uninstallPrometheusGrafana; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.verifyMonExpAppAccess; import static oracle.weblogic.kubernetes.utils.OKDUtils.createRouteForOKD; @@ -134,6 +137,7 @@ class ItMonitoringExporterMetricsFiltering { private static String grafanaReleaseName = "grafana" + releaseSuffix; private static String monitoringExporterDir; private static String hostPortPrometheus = null; + private static String servletPath = "com.oracle.wls.exporter"; /** @@ -223,6 +227,14 @@ public void initAll(@Namespaces(4) List namespaces) throws IOException { } assertDoesNotThrow(() -> setupDomainAndMonitoringTools(domain1Namespace, domain1Uid), "failed to setup domain and monitoring tools"); + + if (!isVersionAtLeast(MONITORING_EXPORTER_WEBAPP_VERSION, "2.3.0")) { + logger.info("Monitoting Exporter Version is less than 2.3.0"); + servletPath = "com.oracle.wls.exporter.webapp"; + } else { + servletPath = servletPath + ((WEBLOGIC_IMAGE_TAG.contains("14.1") + || WEBLOGIC_IMAGE_TAG.contains("12.")) ? ".javax" : ".jakarta"); + } } /** @@ -249,12 +261,21 @@ void testFilterIIncludedKeysFromTopLevel() throws Exception { void testFilterIIncludedKeysFromSubLevel() throws Exception { logger.info("Testing filtering only included specific app name in the metrics "); List checkIncluded = new ArrayList<>(); - checkIncluded.add("servletName=\"com.oracle.wls.exporter.webapp.ExporterServlet\""); + // Regular expression pattern to match servletName="ANYTHING.ExporterServlet" + String checkKey1 = "servletName=\"" + servletPath + ".ExporterServlet\""; + checkIncluded.add(checkKey1); List checkExcluded = new ArrayList<>(); - checkExcluded.add("servletName=\"com.oracle.wls.exporter.webapp.MainServlet\""); - - replaceConfigurationWithFilter(RESOURCE_DIR + "/exporter/rest_filter_included_servlet_name.yaml", + // Regular expression pattern to match servletName="ANYTHING.ExporterServlet" + String checkKey2 = "servletName=\"" + servletPath + ".MainServlet\""; + checkExcluded.add(checkKey2); + + String configurationFile = replaceValueInFile( + "ItMonitoringExporterMetricsFiltering/testFilterIIncludedKeysFromSubLevel", + "rest_filter_included_servlet_name.yaml", + "com.oracle.wls.exporter.webapp", servletPath); + replaceConfigurationWithFilter(configurationFile, checkIncluded, checkExcluded); + } /** @@ -266,13 +287,20 @@ void testFilterIIncludedKeysFromSubLevel() throws Exception { void testFilterIIncludedKeysFromBothLevels() throws Exception { logger.info("Testing filtering only included specific app name in the metrics "); List checkIncluded = new ArrayList<>(); - checkIncluded.add("servletName=\"com.oracle.wls.exporter.webapp.ExporterServlet\""); + String checkKey1 = "servletName=\"" + servletPath + ".ExporterServlet\""; + checkIncluded.add(checkKey1); checkIncluded.add("app=\"wls-exporter\""); List checkExcluded = new ArrayList<>(); - checkExcluded.add("servletName=\"com.oracle.wls.exporter.webapp.MainServlet\""); + String checkKey2 = "servletName=\"" + servletPath + ".MainServlet\""; + checkExcluded.add(checkKey2); checkExcluded.add("app=\"myear1\""); - replaceConfigurationWithFilter(RESOURCE_DIR - + "/exporter/rest_filter_included_webapp_and_servlet_names.yaml",checkIncluded, checkExcluded); + String configurationFile = replaceValueInFile( + "ItMonitoringExporterMetricsFiltering/testFilterIIncludedKeysFromBothLevels", + "rest_filter_included_webapp_and_servlet_names.yaml", + "com.oracle.wls.exporter.webapp", servletPath); + replaceConfigurationWithFilter(configurationFile, + checkIncluded, checkExcluded); + } /** @@ -299,11 +327,16 @@ void testFilterExcludedKeysFromTopLevel() throws Exception { void testFilterExcludedKeysFromSubLevel() throws Exception { logger.info("Testing filtering only excluded specific app name in the metrics "); List checkIncluded = new ArrayList<>(); - checkIncluded.add("servletName=\"com.oracle.wls.exporter.webapp.MainServlet\""); + String checkKey1 = "servletName=\"" + servletPath + ".MainServlet\""; + checkIncluded.add(checkKey1); List checkExcluded = new ArrayList<>(); - checkExcluded.add("servletName=\"com.oracle.wls.exporter.webapp.ExporterServlet\""); - - replaceConfigurationWithFilter(RESOURCE_DIR + "/exporter/rest_filter_excluded_servlet_name.yaml", + String checkKey2 = "servletName=\"" + servletPath + ".ExporterServlet\""; + checkExcluded.add(checkKey2); + String configurationFile = replaceValueInFile( + "ItMonitoringExporterMetricsFiltering/testFilterExcludedKeysFromSubLevel", + "rest_filter_excluded_servlet_name.yaml", + "com.oracle.wls.exporter.webapp", servletPath); + replaceConfigurationWithFilter(configurationFile, checkIncluded, checkExcluded); } @@ -316,13 +349,19 @@ void testFilterExcludedKeysFromSubLevel() throws Exception { void testFilterExcludedKeysFromBothLevels() throws Exception { logger.info("Testing filtering only excluded specific app name in the metrics "); List checkIncluded = new ArrayList<>(); - checkIncluded.add("servletName=\"com.oracle.wls.exporter.webapp.ExporterServlet\""); + String checkKey1 = "servletName=\"" + servletPath + ".ExporterServlet\""; + checkIncluded.add(checkKey1); checkIncluded.add("app=\"myear1\""); List checkExcluded = new ArrayList<>(); - checkExcluded.add("servletName=\"com.oracle.wls.exporter.webapp.MainServlet\""); + String checkKey2 = "servletName=\"" + servletPath + ".MainServlet\""; + checkExcluded.add(checkKey2); checkExcluded.add("app=\"myear123\""); - replaceConfigurationWithFilter(RESOURCE_DIR - + "/exporter/rest_filter_excluded_webapp_and_servlet_names.yaml",checkIncluded, checkExcluded); + String configurationFile = replaceValueInFile( + "ItMonitoringExporterMetricsFiltering/testFilterExcludedKeysFromBothLevels", + "rest_filter_excluded_webapp_and_servlet_names.yaml", + "com.oracle.wls.exporter.webapp", servletPath); + replaceConfigurationWithFilter(configurationFile, + checkIncluded, checkExcluded); } /** @@ -336,9 +375,13 @@ void testFilterIncludedTopExcludedKeysSubLevels() throws Exception { List checkIncluded = new ArrayList<>(); checkIncluded.add("app=\"wls-exporter\""); List checkExcluded = new ArrayList<>(); - checkExcluded.add("servletName=\"com.oracle.wls.exporter.webapp.ExporterServlet\""); - replaceConfigurationWithFilter(RESOURCE_DIR - + "/exporter/rest_filter_included_webapp_excluded_servlet_name.yaml",checkIncluded, checkExcluded); + checkExcluded.add("servletName=\"" + servletPath + ".ExporterServlet\""); + String configurationFile = replaceValueInFile( + "ItMonitoringExporterMetricsFiltering/testFilterIncludedTopExcludedKeysSubLevels", + "rest_filter_included_webapp_excluded_servlet_name.yaml", + "com.oracle.wls.exporter.webapp", servletPath); + replaceConfigurationWithFilter(configurationFile, + checkIncluded, checkExcluded); } /** @@ -368,11 +411,16 @@ void testFilterIncludedExcludedKeysComboTopLevel() throws Exception { void testFilterIncludedExcludedKeysComboSubLevel() throws Exception { logger.info("Testing filtering included and excluded specific app names in the metrics "); List checkIncluded = new ArrayList<>(); - checkIncluded.add("servletName=\"com.oracle.wls.exporter.webapp"); + checkIncluded.add("servletName=\"" + servletPath); List checkExcluded = new ArrayList<>(); - checkExcluded.add("servletName=\"com.oracle.wls.exporter.webapp.ExporterServlet\""); - replaceConfigurationWithFilter(RESOURCE_DIR - + "/exporter/rest_filter_included_excluded_servlet_name.yaml",checkIncluded, checkExcluded); + checkExcluded.add("servletName=\"" + servletPath + ".ExporterServlet\""); + String configurationFile = replaceValueInFile( + "ItMonitoringExporterMetricsFiltering/testFilterIncludedExcludedKeysComboSubLevel", + "rest_filter_included_excluded_servlet_name.yaml", + "com.oracle.wls.exporter.webapp", servletPath); + replaceConfigurationWithFilter(configurationFile, + checkIncluded, checkExcluded); + } /** @@ -728,17 +776,21 @@ private void replaceConfigurationWithFilter(String configurationFile, } private static void verifyMetrics(List checkIncluded, List checkExcluded) { + boolean isRegexInc = false; + boolean isRegexExc = false; for (String includedString : checkIncluded) { + assertTrue(verifyMonExpAppAccess("wls-exporter/metrics", - includedString, + includedString, isRegexInc, domain1Uid, domain1Namespace, false, cluster1Name), "monitoring exporter metrics can't filter to included " + includedString); } for (String excludedString : checkExcluded) { + assertFalse(verifyMonExpAppAccess("wls-exporter/metrics", - excludedString, + excludedString, isRegexExc, domain1Uid, domain1Namespace, false, cluster1Name), @@ -759,7 +811,6 @@ private void appendConfiguration(String configFile) throws Exception { "Page does not contain expected Unable to Update Configuration"); } - private static void installTraefikIngressController() throws IOException { // install and verify Traefik logger.info("Installing Traefik controller using helm"); @@ -781,5 +832,22 @@ private int getTraefikLbNodePort(boolean isHttps) { -> getServiceNodePort(traefikNamespace, traefikHelmParams.getReleaseName(), isHttps ? "websecure" : "web"), "Getting web node port for Traefik loadbalancer failed"); } + + private static boolean isVersionAtLeast(String version, String minVersion) { + String[] versionParts = version.split("\\."); + String[] minVersionParts = minVersion.split("\\."); + + for (int i = 0; i < 3; i++) { + int verPart = Integer.parseInt(versionParts[i]); // Convert to integer + int minVPart = Integer.parseInt(minVersionParts[i]); + + if (verPart > minVPart) { + return true; + } else if (verPart < minVPart) { + return false; + } + } + return true; // Versions are equal + } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index be2a5baa57e..74c7d40a5ab 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -311,7 +311,7 @@ public interface TestConstants { //monitoring constants public static final String MONITORING_EXPORTER_WEBAPP_VERSION = - getNonEmptySystemProperty("wko.it.monitoring.exporter.webapp.version", "2.1.3"); + getNonEmptySystemProperty("wko.it.monitoring.exporter.webapp.version", "2.3.0"); public static final String MONITORING_EXPORTER_BRANCH = getNonEmptySystemProperty("wko.it.monitoring.exporter.branch", "main"); public static final String PROMETHEUS_CHART_VERSION = diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index dc610e5de5a..814647ae072 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2024, Oracle and/or its affiliates. +// Copyright (c) 2021, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.utils; @@ -18,6 +18,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1ConfigMap; @@ -89,6 +91,7 @@ import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_PUSHGATEWAY_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_REPO_NAME; import static oracle.weblogic.kubernetes.TestConstants.PROMETHEUS_REPO_URL; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER_DEFAULT; @@ -146,7 +149,8 @@ public class MonitoringUtils { /** * Download monitoring exporter webapp wls-exporter.war based on provided version * and insert provided configuration. - * @param configFile configuration to monitor Weblogic Domain + * + * @param configFile configuration to monitor Weblogic Domain * @param applicationDir location where application war file will be created */ public static void downloadMonitoringExporterApp(String configFile, String applicationDir) { @@ -177,11 +181,11 @@ public static void downloadMonitoringExporterApp(String configFile, String appli testUntil( (() -> Command - .withParams( - new CommandParams() - .verbose(true) - .command(command1)) - .executeAndVerify("adding: config.yml") + .withParams( + new CommandParams() + .verbose(true) + .command(command1)) + .executeAndVerify("adding: config.yml") ), logger, "Downloading monitoring exporter webapp"); @@ -192,9 +196,10 @@ public static void downloadMonitoringExporterApp(String configFile, String appli /** * Build monitoring exporter web applicaiont wls-exporter.war with provided configuration. + * * @param monitoringExporterSrcDir directory containing github monitoring exporter - * @param configFile configuration file for weblogic domain monitoring - * @param appDir directory where war file will be created + * @param configFile configuration file for weblogic domain monitoring + * @param appDir directory where war file will be created */ public static void buildMonitoringExporterApp(String monitoringExporterSrcDir, String configFile, String appDir) { @@ -218,6 +223,7 @@ public static void buildMonitoringExporterApp(String monitoringExporterSrcDir, S /** * Clone monitoring exporter github src. + * * @param monitoringExporterSrcDir directory containing github monitoring exporter */ public static void cloneMonitoringExporter(String monitoringExporterSrcDir) { @@ -244,8 +250,8 @@ public static void cloneMonitoringExporter(String monitoringExporterSrcDir) { /** * Check metrics using Prometheus. * - * @param searchKey - metric query expression - * @param expectedVal - expected metrics to search + * @param searchKey - metric query expression + * @param expectedVal - expected metrics to search * @param hostPortPrometheus host:nodePort for prometheus * @throws Exception if command to check metrics fails */ @@ -257,7 +263,7 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa // url String curlCmd = String.format("curl -g --silent --show-error --noproxy '*' -v " - + " --max-time 60 -H 'host: *'" + + " --max-time 60 -H 'host: *'" + " http://%s/api/v1/query?query=%s", hostPortPrometheus, searchKey); @@ -291,8 +297,8 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa /** * Check metrics using Prometheus. * - * @param searchKey - metric query expression - * @param expectedVal - expected metrics to search + * @param searchKey - metric query expression + * @param expectedVal - expected metrics to search * @param hostPortPrometheus host:nodePort for prometheus * @throws Exception if command to check metrics fails */ @@ -321,7 +327,7 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa /** * Check output of the command against expected output. * - * @param cmd command + * @param cmd command * @param searchKey expected response from the command * @return true if the command succeeds */ @@ -338,7 +344,7 @@ public static boolean execCommandCheckResponse(String cmd, String searchKey) { /** * Check if executed command contains expected output. * - * @param cmd command to execute + * @param cmd command to execute * @param searchKey expected output * @return true if the output matches searchKey otherwise false */ @@ -348,10 +354,11 @@ public static Callable searchForKey(String cmd, String searchKey) { /** * Edit Prometheus Config Map. - * @param oldRegex search for existed value to replace - * @param newRegex new value + * + * @param oldRegex search for existed value to replace + * @param newRegex new value * @param prometheusNS namespace for prometheus pod - * @param cmName name of Config Map to modify + * @param cmName name of Config Map to modify * @throws ApiException when update fails */ public static void editPrometheusCM(String oldRegex, String newRegex, @@ -362,9 +369,9 @@ public static void editPrometheusCM(String oldRegex, String newRegex, .findAny() .orElse(null); - assertNotNull(promCm,"Can't find cm for " + cmName); + assertNotNull(promCm, "Can't find cm for " + cmName); Map cmData = promCm.getData(); - String values = cmData.get("prometheus.yml").replace(oldRegex,newRegex); + String values = cmData.get("prometheus.yml").replace(oldRegex, newRegex); assertNotNull(values, "can't find values for key prometheus.yml"); cmData.replace("prometheus.yml", values); @@ -378,7 +385,7 @@ public static void editPrometheusCM(String oldRegex, String newRegex, .findAny() .orElse(null); - assertNotNull(promCm,"Can't find cm for " + cmName); + assertNotNull(promCm, "Can't find cm for " + cmName); assertNotNull(promCm.getData(), "Can't retreive the cm data for " + cmName + " after modification"); } @@ -386,11 +393,11 @@ public static void editPrometheusCM(String oldRegex, String newRegex, /** * Install Prometheus and wait up to five minutes until the prometheus pods are ready. * - * @param promReleaseSuffix the prometheus release name unique suffix - * @param promNamespace the prometheus namespace in which the prometheus will be installed - * @param promVersion the version of the prometheus helm chart + * @param promReleaseSuffix the prometheus release name unique suffix + * @param promNamespace the prometheus namespace in which the prometheus will be installed + * @param promVersion the version of the prometheus helm chart * @param prometheusRegexValue string (namespace;domainuid) to manage specific domain, - * default is regex: default;domain1 + * default is regex: default;domain1 * @return the prometheus Helm installation parameters */ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuffix, @@ -399,38 +406,38 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff String prometheusRegexValue, String promHelmValuesFile) { return installAndVerifyPrometheus(promReleaseSuffix, - promNamespace, - promVersion, - prometheusRegexValue, - promHelmValuesFile, - null); + promNamespace, + promVersion, + prometheusRegexValue, + promHelmValuesFile, + null); } /** * Install Prometheus and wait up to five minutes until the prometheus pods are ready. * - * @param promReleaseSuffix the prometheus release name unigue suffix - * @param promNamespace the prometheus namespace in which the operator will be installed - * @param promVersion the version of the prometheus helm chart - * @param prometheusRegexValue string (namespace;domainuid) to manage specific domain, - * default is regex: default;domain1 + * @param promReleaseSuffix the prometheus release name unigue suffix + * @param promNamespace the prometheus namespace in which the operator will be installed + * @param promVersion the version of the prometheus helm chart + * @param prometheusRegexValue string (namespace;domainuid) to manage specific domain, + * default is regex: default;domain1 * @param promHelmValuesFileDir path to prometheus helm values file directory - * @param webhookNS namespace for webhook namespace - * @param ports optional prometheus and alert manager ports + * @param webhookNS namespace for webhook namespace + * @param ports optional prometheus and alert manager ports * @return the prometheus Helm installation parameters */ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuffix, - String promNamespace, - String promVersion, - String prometheusRegexValue, - String promHelmValuesFileDir, - String webhookNS, - int...ports) { + String promNamespace, + String promVersion, + String prometheusRegexValue, + String promHelmValuesFileDir, + String webhookNS, + int... ports) { LoggingFacade logger = getLogger(); String prometheusReleaseName = "prometheus" + promReleaseSuffix; logger.info("create a staging location for prometheus scripts"); Path fileTemp = Paths.get(promHelmValuesFileDir); - assertDoesNotThrow(() -> FileUtils.deleteDirectory(fileTemp.toFile()),"Failed to delete temp dir for prometheus"); + assertDoesNotThrow(() -> FileUtils.deleteDirectory(fileTemp.toFile()), "Failed to delete temp dir for prometheus"); assertDoesNotThrow(() -> Files.createDirectories(fileTemp), "Failed to create temp dir for prometheus"); String promValuesFile = OKE_CLUSTER_PRIVATEIP ? "promvaluesoke.yaml" : "promvalues.yaml"; @@ -438,7 +445,7 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff Path srcPromFile = Paths.get(RESOURCE_DIR, "exporter", promValuesFile); Path targetPromFile = Paths.get(fileTemp.toString(), "promvalues.yaml"); assertDoesNotThrow(() -> Files.copy(srcPromFile, targetPromFile, - StandardCopyOption.REPLACE_EXISTING)," Failed to copy files"); + StandardCopyOption.REPLACE_EXISTING), " Failed to copy files"); String oldValue = "regex: default;domain1"; assertDoesNotThrow(() -> { replaceStringInFile(targetPromFile.toString(), @@ -484,8 +491,8 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff if (webhookNS != null) { //replace with webhook ns assertDoesNotThrow(() -> replaceStringInFile(targetPromFile.toString(), - "webhook.webhook.svc.cluster.local", - String.format("webhook.%s.svc.cluster.local", webhookNS)), "Failed to replace String "); + "webhook.webhook.svc.cluster.local", + String.format("webhook.%s.svc.cluster.local", webhookNS)), "Failed to replace String "); } if (OKD) { assertDoesNotThrow(() -> replaceStringInFile(targetPromFile.toString(), @@ -548,13 +555,13 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff // wait for the promethues pods to be ready logger.info("Wait for the promethues pod is ready in namespace {0}", promNamespace); testUntil( - assertDoesNotThrow(() -> isPrometheusReady(promNamespace,prometheusReleaseName), - "prometheusIsReady failed with ApiException"), + assertDoesNotThrow(() -> isPrometheusReady(promNamespace, prometheusReleaseName), + "prometheusIsReady failed with ApiException"), logger, "prometheus to be running in namespace {0}", promNamespace); String command1 = KUBERNETES_CLI + " get svc -n " + promNamespace; - assertDoesNotThrow(() -> ExecCommand.exec(command1,true)); + assertDoesNotThrow(() -> ExecCommand.exec(command1, true)); String command2 = KUBERNETES_CLI + " describe svc -n " + promNamespace; assertDoesNotThrow(() -> ExecCommand.exec(command2, true)); return prometheusParams; @@ -564,11 +571,11 @@ public static PrometheusParams installAndVerifyPrometheus(String promReleaseSuff * Install Prometheus adapter and wait up to five minutes until the prometheus adapter pods are ready. * * @param promAdapterReleaseName the prometheus adapter release name - * @param promAdapterNamespace the prometheus adapter namespace + * @param promAdapterNamespace the prometheus adapter namespace * @return the prometheus adapter Helm installation parameters */ public static HelmParams installAndVerifyPrometheusAdapter(String promAdapterReleaseName, - String promAdapterNamespace, + String promAdapterNamespace, String prometheusHost, int prometheusPort) { LoggingFacade logger = getLogger(); @@ -622,8 +629,8 @@ public static HelmParams installAndVerifyPrometheusAdapter(String promAdapterRel String podName = assertDoesNotThrow(() -> getPodName(promAdapterNamespace, "prometheus-adapterhpatest"), "Can't find prometheus-adapter pod"); - checkPodExists(podName,null, promAdapterNamespace); - String command1 = KUBERNETES_CLI + " describe pods " + podName + " -n " + promAdapterNamespace; + checkPodExists(podName, null, promAdapterNamespace); + String command1 = KUBERNETES_CLI + " describe pods " + podName + " -n " + promAdapterNamespace; assertDoesNotThrow(() -> { ExecResult result = ExecCommand.exec(command1, true); String response = result.stdout().trim(); @@ -645,10 +652,10 @@ public static HelmParams installAndVerifyPrometheusAdapter(String promAdapterRel /** * Install Grafana and wait up to five minutes until the grafana pod is ready. * - * @param grafanaReleaseName the grafana release name - * @param grafanaNamespace the grafana namespace in which the operator will be installed + * @param grafanaReleaseName the grafana release name + * @param grafanaNamespace the grafana namespace in which the operator will be installed * @param grafanaHelmValuesFileDir the grafana helm values.yaml file directory - * @param grafanaVersion the version of the grafana helm chart + * @param grafanaVersion the version of the grafana helm chart * @return the grafana Helm installation parameters */ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, @@ -658,7 +665,7 @@ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, LoggingFacade logger = getLogger(); logger.info("create a staging location for grafana scripts"); Path fileTemp = Paths.get(grafanaHelmValuesFileDir); - assertDoesNotThrow(() -> FileUtils.deleteDirectory(fileTemp.toFile()),"Failed to delete temp dir for grafana"); + assertDoesNotThrow(() -> FileUtils.deleteDirectory(fileTemp.toFile()), "Failed to delete temp dir for grafana"); assertDoesNotThrow(() -> Files.createDirectories(fileTemp), "Failed to create temp dir for grafana"); String grafanavaluesFile = OKE_CLUSTER_PRIVATEIP ? "grafanavaluesoke.yaml" : "grafanavalues.yaml"; @@ -666,24 +673,24 @@ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, Path srcGrafanaFile = Paths.get(RESOURCE_DIR, "exporter", grafanavaluesFile); Path targetGrafanaFile = Paths.get(fileTemp.toString(), "grafanavalues.yaml"); assertDoesNotThrow(() -> Files.copy(srcGrafanaFile, targetGrafanaFile, - StandardCopyOption.REPLACE_EXISTING)," Failed to copy files"); + StandardCopyOption.REPLACE_EXISTING), " Failed to copy files"); assertDoesNotThrow(() -> replaceStringInFile(targetGrafanaFile.toString(), - "pvc-grafana", "pvc-" + grafanaReleaseName)); + "pvc-grafana", "pvc-" + grafanaReleaseName)); assertDoesNotThrow(() -> replaceStringInFile(targetGrafanaFile.toString(), "grafana_image", - GRAFANA_IMAGE_NAME),"Failed to replace String "); + GRAFANA_IMAGE_NAME), "Failed to replace String "); assertDoesNotThrow(() -> replaceStringInFile(targetGrafanaFile.toString(), "grafana_tag", - GRAFANA_IMAGE_TAG),"Failed to replace String "); + GRAFANA_IMAGE_TAG), "Failed to replace String "); assertDoesNotThrow(() -> replaceStringInFile(targetGrafanaFile.toString(), "busybox_image", BUSYBOX_IMAGE), "Failed to replace String "); assertDoesNotThrow(() -> replaceStringInFile(targetGrafanaFile.toString(), "busybox_tag", - BUSYBOX_TAG), "Failed to replace String "); + BUSYBOX_TAG), "Failed to replace String "); if (!OKE_CLUSTER) { assertDoesNotThrow(() -> replaceStringInFile(targetGrafanaFile.toString(), - "enabled: false", "enabled: true")); + "enabled: false", "enabled: true")); } // Helm install parameters HelmParams grafanaHelmParams = new HelmParams() @@ -730,10 +737,10 @@ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, } boolean isGrafanaInstalled = false; if (OKD) { - addSccToDBSvcAccount(grafanaReleaseName,grafanaNamespace); + addSccToDBSvcAccount(grafanaReleaseName, grafanaNamespace); } assertTrue(installGrafana(grafanaParams), - String.format("Failed to install grafana in namespace %s",grafanaNamespace)); + String.format("Failed to install grafana in namespace %s", grafanaNamespace)); logger.info("Grafana installed in namespace {0}", grafanaNamespace); // list Helm releases matching grafana release name in namespace @@ -749,13 +756,12 @@ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, logger.info("Wait for the grafana pod is ready in namespace {0}", grafanaNamespace); testUntil( assertDoesNotThrow(() -> isGrafanaReady(grafanaNamespace), - "grafanaIsReady failed with ApiException"), + "grafanaIsReady failed with ApiException"), logger, "grafana to be running in namespace {0}", grafanaNamespace); - //return grafanaHelmParams; return grafanaParams; } @@ -763,7 +769,7 @@ public static GrafanaParams installAndVerifyGrafana(String grafanaReleaseName, /** * Extra clean up for Prometheus and Grafana artifacts. * - * @param grafanaReleaseName the grafana release name + * @param grafanaReleaseName the grafana release name * @param prometheusReleaseName prometheus release name */ public static void cleanupPromGrafanaClusterRoles(String prometheusReleaseName, String grafanaReleaseName) { @@ -809,7 +815,6 @@ public static void cleanupPromGrafanaClusterRoles(String prometheusReleaseName, /** * Extra clean up for Prometheus Adapter artifacts. - * */ public static void cleanupPrometheusAdapterClusterRoles() { //extra cleanup @@ -855,7 +860,7 @@ public static void installMonitoringExporter(String monitoringExporterDir) { /** * Download src from monitoring exporter github project and build or install webapp. * - * @param monitoringExporterDir full path to monitoring exporter install location + * @param monitoringExporterDir full path to monitoring exporter install location * @param toBuildMonitoringExporter if true build monitoring exporter webapp or download if false. */ public static void installMonitoringExporter(String monitoringExporterDir, boolean toBuildMonitoringExporter) { @@ -888,9 +893,9 @@ public static void installMonitoringExporter(String monitoringExporterDir, boole } else { buildMonitoringExporterApp(monitoringExporterSrcDir, RESOURCE_DIR + "/exporter/exporter-config.yaml", monitoringExporterAppDir); - buildMonitoringExporterApp(monitoringExporterSrcDir,RESOURCE_DIR + buildMonitoringExporterApp(monitoringExporterSrcDir, RESOURCE_DIR + "/exporter/exporter-config-norestport.yaml", monitoringExporterAppNoRestPortDir); - buildMonitoringExporterApp(monitoringExporterSrcDir,RESOURCE_DIR + buildMonitoringExporterApp(monitoringExporterSrcDir, RESOURCE_DIR + "/exporter/exporter-config-administrationrestport.yaml", monitoringExporterAppAdministrationRestPortDir); } logger.info("Finished to build Monitoring Exporter webapp."); @@ -913,10 +918,11 @@ public static void deleteMonitoringExporterTempDir(String monitoringExporterDir) /** * Create mii image with monitoring exporter webapp and one more app. + * * @param modelFilePath - path to model file - * @param monexpAppDir - location for monitoring exporter webapp - * @param appName -extra app name - * @param imageName - desired imagename + * @param monexpAppDir - location for monitoring exporter webapp + * @param appName -extra app name + * @param imageName - desired imagename */ public static String createAndVerifyMiiImage(String monexpAppDir, String modelFilePath, String appName, String imageName) { @@ -940,10 +946,11 @@ public static String createAndVerifyMiiImage(String monexpAppDir, String modelFi /** * Create mii image with monitoring exporter webapp and one more app. - * @param modelList - list of the paths to model files + * + * @param modelList - list of the paths to model files * @param monexpAppDir - location for monitoring exporter webapp - * @param appName1 -extra app names - * @param imageName - desired imagename + * @param appName1 -extra app names + * @param imageName - desired imagename */ public static String createAndVerifyMiiImage(String monexpAppDir, List modelList, String appName1, String appName2, @@ -970,7 +977,7 @@ public static String createAndVerifyMiiImage(String monexpAppDir, List m /** * Uninstall Prometheus and Grafana helm charts. * - * @param promHelmParams -helm chart params for prometheus + * @param promHelmParams -helm chart params for prometheus * @param grafanaHelmParams - helm chart params for grafana */ public static void uninstallPrometheusGrafana(HelmParams promHelmParams, GrafanaParams grafanaHelmParams) { @@ -984,7 +991,7 @@ public static void uninstallPrometheusGrafana(HelmParams promHelmParams, Grafana if (grafanaHelmParams != null) { grafanaReleaseName = grafanaHelmParams.getHelmParams().getReleaseName(); Grafana.uninstall(grafanaHelmParams.getHelmParams()); - deleteSecret("grafana-secret",grafanaHelmParams.getHelmParams().getNamespace()); + deleteSecret("grafana-secret", grafanaHelmParams.getHelmParams().getNamespace()); logger.info("Grafana is uninstalled"); } cleanupPromGrafanaClusterRoles(prometheusReleaseName, grafanaReleaseName); @@ -993,17 +1000,17 @@ public static void uninstallPrometheusGrafana(HelmParams promHelmParams, Grafana /** * Create Domain Cr and verity. * - * @param adminSecretName WebLogic admin credentials - * @param repoSecretName image repository secret name + * @param adminSecretName WebLogic admin credentials + * @param repoSecretName image repository secret name * @param encryptionSecretName model encryption secret name - * @param miiImage model in image name - * @param domainUid domain uid - * @param namespace namespace - * @param domainHomeSource domain home source type - * @param replicaCount replica count for the cluster - * @param twoClusters boolean indicating if the domain has 2 clusters - * @param monexpConfig monitoring exporter config - * @param exporterImage exporter image + * @param miiImage model in image name + * @param domainUid domain uid + * @param namespace namespace + * @param domainHomeSource domain home source type + * @param replicaCount replica count for the cluster + * @param twoClusters boolean indicating if the domain has 2 clusters + * @param monexpConfig monitoring exporter config + * @param exporterImage exporter image */ public static void createDomainCrAndVerify(String adminSecretName, String repoSecretName, @@ -1075,7 +1082,7 @@ public static void createDomainCrAndVerify(String adminSecretName, // set cluster references domain.getSpec().withCluster(new V1LocalObjectReference().name(clusterResName)); } - + setPodAntiAffinity(domain); // create domain using model in image logger.info("Create model in image domain {0} in namespace {1} using image {2}", @@ -1100,27 +1107,27 @@ public static void createDomainCrAndVerify(String adminSecretName, } createDomainAndVerify(domain, namespace); } - + /** * create domain from provided image and monitoring exporter sidecar and verify it's start. * - * @param miiImage model in image name - * @param domainUid domain uid - * @param namespace namespace + * @param miiImage model in image name + * @param domainUid domain uid + * @param namespace namespace * @param domainHomeSource domain home source type - * @param replicaCount replica count for the cluster - * @param twoClusters boolean indicating if the domain has 2 clusters - * @param monexpConfig monitoring exporter config - * @param exporterImage exporter image + * @param replicaCount replica count for the cluster + * @param twoClusters boolean indicating if the domain has 2 clusters + * @param monexpConfig monitoring exporter config + * @param exporterImage exporter image */ public static void createAndVerifyDomain(String miiImage, - String domainUid, - String namespace, - String domainHomeSource, - int replicaCount, - boolean twoClusters, - String monexpConfig, - String exporterImage) { + String domainUid, + String namespace, + String domainHomeSource, + int replicaCount, + boolean twoClusters, + String monexpConfig, + String exporterImage) { createAndVerifyDomain(miiImage, domainUid, namespace, @@ -1134,15 +1141,15 @@ public static void createAndVerifyDomain(String miiImage, /** * create domain from provided image and monitoring exporter sidecar and verify it's start. * - * @param miiImage model in image name - * @param domainUid domain uid - * @param namespace namespace + * @param miiImage model in image name + * @param domainUid domain uid + * @param namespace namespace * @param domainHomeSource domain home source type - * @param replicaCount replica count for the cluster - * @param twoClusters boolean indicating if the domain has 2 clusters - * @param monexpConfig monitoring exporter config - * @param exporterImage exporter image - * @param checkPodsReady true or false if test need to check pods status + * @param replicaCount replica count for the cluster + * @param twoClusters boolean indicating if the domain has 2 clusters + * @param monexpConfig monitoring exporter config + * @param exporterImage exporter image + * @param checkPodsReady true or false if test need to check pods status */ public static void createAndVerifyDomain(String miiImage, String domainUid, @@ -1174,7 +1181,7 @@ public static void createAndVerifyDomain(String miiImage, // create domain and verify logger.info("Create model in image domain {0} in namespace {1} using image {2}", domainUid, namespace, miiImage); - createDomainCrAndVerify(adminSecretName, TEST_IMAGES_REPO_SECRET_NAME, encryptionSecretName, miiImage,domainUid, + createDomainCrAndVerify(adminSecretName, TEST_IMAGES_REPO_SECRET_NAME, encryptionSecretName, miiImage, domainUid, namespace, domainHomeSource, replicaCount, twoClusters, monexpConfig, exporterImage); if (checkPodsReady) { String adminServerPodName = domainUid + "-admin-server"; @@ -1212,12 +1219,12 @@ public static void createAndVerifyDomain(String miiImage, /** * Verify the monitoring exporter app can be accessed from all managed servers in the domain through NGINX. * - * @param nginxHost nginx host name + * @param nginxHost nginx host name * @param replicaCount number of managed servers - * @param nodeport nginx nodeport + * @param nodeport nginx nodeport */ - public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int replicaCount, - int nodeport) throws UnknownHostException { + public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int replicaCount, + int nodeport) throws UnknownHostException { List managedServerNames = new ArrayList<>(); for (int i = 1; i <= replicaCount; i++) { @@ -1231,7 +1238,7 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli } String curlCmd = String.format("curl -g --silent --show-error --noproxy '*' -v " - + " --max-time 60 -H 'host: %s' http://%s:%s@%s:%s/wls-exporter/metrics", + + " --max-time 60 -H 'host: %s' http://%s:%s@%s:%s/wls-exporter/metrics", nginxHost, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, @@ -1247,9 +1254,9 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli /** * Verify the monitoring exporter app can be accessed from all managed servers in the domain through NGINX. * - * @param nginxHost nginx host name + * @param nginxHost nginx host name * @param replicaCount number of managed servers - * @param hostPort host:port combo or host string + * @param hostPort host:port combo or host string */ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int replicaCount, String hostPort) { @@ -1261,7 +1268,7 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli // check that NGINX can access the sample apps from all managed servers in the domain String curlCmd = String.format("curl -g --silent --show-error --noproxy '*' -v " - + " --max-time 60 -H 'host: %s' http://%s:%s@%s/wls-exporter/metrics", + + " --max-time 60 -H 'host: %s' http://%s:%s@%s/wls-exporter/metrics", nginxHost, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, @@ -1277,7 +1284,7 @@ public static void verifyMonExpAppAccessThroughNginx(String nginxHost, int repli * Verify the monitoring exporter app can be accessed from all managed servers in the domain through NGINX. * * @param replicaCount number of managed servers - * @param hostPort host:port combination to access app + * @param hostPort host:port combination to access app */ public static void verifyMonExpAppAccess(int replicaCount, String hostPort) { @@ -1289,7 +1296,7 @@ public static void verifyMonExpAppAccess(int replicaCount, String hostPort) { // check the access to monitoring exporter apps from all managed servers in the domain String curlCmd = String.format("curl -g --silent --show-error --noproxy '*' -v " - + " --max-time 60 http://%s:%s@%s/wls-exporter/metrics", + + " --max-time 60 http://%s:%s@%s/wls-exporter/metrics", ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, hostPort); @@ -1304,15 +1311,34 @@ public static void verifyMonExpAppAccess(int replicaCount, String hostPort) { /** * Verify the monitoring exporter app can be accessed from all managed servers in the domain * through direct access to managed server dashboard. + * * @param clusterName - name of cluster - * @param domainNS - domain namespace - * @param domainUid - domain uid - * @param isHttps - protocol - * @param uri - weburl - * @param searchKey - search key in response + * @param domainNS - domain namespace + * @param domainUid - domain uid + * @param isHttps - protocol + * @param uri - weburl + * @param searchKey - search key in response */ public static boolean verifyMonExpAppAccess(String uri, String searchKey, String domainUid, - String domainNS, boolean isHttps, String clusterName) { + String domainNS, boolean isHttps, String clusterName) { + return verifyMonExpAppAccess(uri, searchKey, false, domainUid, + domainNS, isHttps, clusterName); + } + + /** + * Verify the monitoring exporter app can be accessed from all managed servers in the domain + * through direct access to managed server dashboard. + * + * @param clusterName - name of cluster + * @param domainNS - domain namespace + * @param domainUid - domain uid + * @param isHttps - protocol + * @param uri - weburl + * @param searchKey - search key in response + * @param isRegex - search key contains regex + */ + public static boolean verifyMonExpAppAccess(String uri, String searchKey, Boolean isRegex, String domainUid, + String domainNS, boolean isHttps, String clusterName) { String protocol = "http"; String port = "8001"; if (isHttps) { @@ -1338,7 +1364,11 @@ public static boolean verifyMonExpAppAccess(String uri, String searchKey, String String response = result.stdout().trim(); logger.info("Response : exitValue {0}, stdout {1}, stderr {2}", result.exitValue(), response, result.stderr()); - isFound = response.contains(searchKey); + if (isRegex) { + isFound = containsValidServletName(response, searchKey); + } else { + isFound = response.contains(searchKey); + } logger.info("isFound value:" + isFound); } catch (Exception ex) { logger.info("Can't execute command " + command + Arrays.toString(ex.getStackTrace())); @@ -1347,15 +1377,25 @@ public static boolean verifyMonExpAppAccess(String uri, String searchKey, String return isFound; } + // Method to check if the string contains the required pattern + // Regular expression pattern to match servletName="ANYTHING.ExporterServlet" + // regex = "servletName=\"[^\"]*ExporterServlet\""; + private static boolean containsValidServletName(String input, String regex) { + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(input); + return matcher.find(); + } + /** * Verify the monitoring exporter sidecar can be accessed from all managed servers in the domain * through direct access to managed server dashboard. - * @param domainNS - domain namespace - * @param podName - managed server pod name - * @param searchKey - search key in response + * + * @param domainNS - domain namespace + * @param podName - managed server pod name + * @param searchKey - search key in response */ public static boolean verifyMonExpAppAccessSideCar(String searchKey, - String domainNS, String podName) { + String domainNS, String podName) { // access metrics final String command = String.format( @@ -1382,16 +1422,17 @@ public static boolean verifyMonExpAppAccessSideCar(String searchKey, /** * Build exporter, create image with unique name, create corresponding repo secret and push to registry. * - * @param srcDir directory where source is located - * @param baseImageName base image name - * @param namespace image namespace - * @param secretName repo secretname for image + * @param srcDir directory where source is located + * @param baseImageName base image name + * @param namespace image namespace + * @param secretName repo secretname for image * @param extraImageBuilderArgs user specified extra args * @return image name */ - public static String buildMonitoringExporterCreateImageAndPushToRepo(String srcDir, String baseImageName, - String namespace, String secretName, - String extraImageBuilderArgs) throws ApiException { + public static String buildMonitoringExporterCreateImageAndPushToRepo( + String srcDir, String baseImageName, + String namespace, String secretName, + String extraImageBuilderArgs) throws ApiException { String command = String.format("cd %s && mvn clean install -Dmaven.test.skip=true", srcDir); logger.info("Executing command " + command); assertTrue(Command @@ -1437,7 +1478,7 @@ public static void createTraefikIngressRoutingRulesForMonitoring(String namespac /** * Delete Traefik Ingress routing rules for prometheus. * - * @param dstFile path for ingress rule deployment + * @param dstFile path for ingress rule deployment */ public static void deleteTraefikIngressRoutingRules(Path dstFile) { @@ -1457,4 +1498,36 @@ public static void deleteTraefikIngressRoutingRules(Path dstFile) { Files.deleteIfExists(dstFile); }); } + + /** + * Replace Value In File. + * + * @param tempFileName file name + * @param newValue new value to search + * @param oldValue old value to replace + * @param srcFileName name of source file in exporter dir + * @return modified file path + */ + public static String replaceValueInFile(String tempFileName, String srcFileName, String oldValue, String newValue) { + + String tempFileDir = Paths.get(RESULTS_ROOT, + tempFileName).toString(); + Path fileTemp = Paths.get(tempFileDir); + assertDoesNotThrow(() -> FileUtils.deleteDirectory(fileTemp.toFile()), "Failed to delete temp dir "); + + assertDoesNotThrow(() -> Files.createDirectories(fileTemp), "Failed to create temp dir "); + + logger.info("copy the " + srcFileName + " to staging location"); + Path srcFile = Paths.get(RESOURCE_DIR, "exporter", srcFileName); + Path targetFile = Paths.get(fileTemp.toString(), srcFileName); + assertDoesNotThrow(() -> Files.copy(srcFile, targetFile, + StandardCopyOption.REPLACE_EXISTING), " Failed to copy files"); + + assertDoesNotThrow(() -> { + replaceStringInFile(targetFile.toString(), + oldValue, + newValue); + }); + return targetFile.toString(); + } } From 168cf30c28858ee145bb16418211943087a3b6d9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 24 Mar 2025 14:56:28 -0400 Subject: [PATCH 298/356] Revert "Fix domain delayed response" This reverts commit 5e9d74af5244e26618893e72ae036dfd21572b18. --- .../operator/DomainProcessorImpl.java | 8 +- .../kubernetes/operator/LabelConstants.java | 1 - .../operator/helpers/PodHelper.java | 87 ------------------- .../steps/ShutdownManagedServerStep.java | 21 +++-- .../operator/DomainProcessorTest.java | 1 - .../operator/helpers/PodPresenceTest.java | 18 +++- .../steps/ShutdownManagedServerStepTest.java | 10 +-- 7 files changed, 29 insertions(+), 117 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java b/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java index 12770777117..3a6f8a8cdd6 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainProcessorImpl.java @@ -601,8 +601,7 @@ private void processServerPodWatch(V1Pod pod, String watchType) { // fall through case MODIFIED: boolean podPreviouslyEvicted = info.setServerPodFromEvent(serverName, pod, PodHelper::isEvicted); - boolean isEvicted = PodHelper.isEvicted(pod); - if (isEvicted && !podPreviouslyEvicted) { + if (PodHelper.isEvicted(pod) && !podPreviouslyEvicted) { if (PodHelper.shouldRestartEvictedPod(pod)) { LOGGER.info(MessageKeys.POD_EVICTED, getPodName(pod), getPodStatusMessage(pod)); createMakeRightOperation(info).interrupt().withExplicitRecheck().execute(); @@ -610,11 +609,6 @@ private void processServerPodWatch(V1Pod pod, String watchType) { LOGGER.info(MessageKeys.POD_EVICTED_NO_RESTART, getPodName(pod), getPodStatusMessage(pod)); } } - boolean isReady = PodHelper.isReady(pod); - boolean isLabeledForShutdown = PodHelper.isPodAlreadyAnnotatedForShutdown(pod); - if ((isEvicted || isReady != isLabeledForShutdown || PodHelper.isFailed(pod)) && !PodHelper.isDeleting(pod)) { - createMakeRightOperation(info).interrupt().withExplicitRecheck().execute(); - } boolean isUnschedulable = PodHelper.hasUnSchedulableCondition(pod); if (isUnschedulable) { LOGGER.info(POD_UNSCHEDULABLE, getPodName(pod), getUnSchedulableConditionMessage(pod)); diff --git a/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java b/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java index 7886c8da4cb..9a49e26be06 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/LabelConstants.java @@ -25,7 +25,6 @@ public interface LabelConstants { String MII_UPDATED_RESTART_REQUIRED_LABEL = "weblogic.configChangesPendingRestart"; String INTROSPECTION_DOMAIN_SPEC_GENERATION = "weblogic.domainSpecGeneration"; String TO_BE_ROLLED_LABEL = "weblogic.awaitingPodRoll"; - String TO_BE_SHUTDOWN_LABEL = "weblogic.awaitingShutdown"; String DOMAIN_OBSERVED_GENERATION_LABEL = "weblogic.domainObservedGeneration"; String CLUSTER_OBSERVED_GENERATION_LABEL = "weblogic.clusterObservedGeneration"; String SERVICE_TYPE_LABEL = "serviceType"; diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java index 9234e90ae9e..2bb1b046e65 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodHelper.java @@ -7,7 +7,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -49,7 +48,6 @@ import oracle.kubernetes.weblogic.domain.model.ServerStatus; import oracle.kubernetes.weblogic.domain.model.Shutdown; -import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static oracle.kubernetes.operator.KubernetesConstants.EVICTED_REASON; import static oracle.kubernetes.operator.KubernetesConstants.POD_SCHEDULED; import static oracle.kubernetes.operator.KubernetesConstants.UNSCHEDULABLE_REASON; @@ -58,7 +56,6 @@ import static oracle.kubernetes.operator.ProcessingConstants.SERVERS_TO_ROLL; import static oracle.kubernetes.operator.WebLogicConstants.SHUTDOWN_STATE; import static oracle.kubernetes.operator.WebLogicConstants.UNKNOWN_STATE; -import static oracle.kubernetes.operator.helpers.PodDisruptionBudgetHelper.getDomainUid; @SuppressWarnings("ConstantConditions") public class PodHelper { @@ -131,7 +128,6 @@ public static boolean isWaitingToRoll(V1Pod pod) { .orElse(false); } - static boolean hasReadyServer(V1Pod pod) { return Optional.ofNullable(pod).map(PodHelper::hasReadyStatus).orElse(false); } @@ -397,89 +393,6 @@ public static String getPodLabel(V1Pod pod, String labelName) { .orElse(null); } - - /** - * get pod's annotation value for a annotation name. - * @param pod pod - * @param annotationName annotation name - * @return annotation value - */ - public static String getPodAnnotation(V1Pod pod, String annotationName) { - return Optional.ofNullable(pod) - .map(V1Pod::getMetadata) - .map(V1ObjectMeta::getAnnotations) - .map(m -> m.get(annotationName)) - .orElse(null); - } - - private static boolean hasAnnotation(V1Pod pod, String annotation) { - return Optional.ofNullable(pod).map(V1Pod::getMetadata).map(V1ObjectMeta::getAnnotations) - .map(l -> l.containsKey(annotation)).orElse(false); - } - - private static Step patchPodAnnotation(V1Pod pod, String annotation, String value, Step next) { - - if (!hasAnnotation(pod, annotation)) { - JsonPatchBuilder patchBuilder = Json.createPatchBuilder(); - patchBuilder.add("/metadata/annotations/" + annotation, value); - new CallBuilder() - .patchPodAsync(pod.getMetadata().getName(), pod.getMetadata().getNamespace(), - pod.getMetadata().getLabels().get(LabelConstants.DOMAINUID_LABEL), - new V1Patch(patchBuilder.build().toString()), - patchResponse(next)); - } - return next; - } - - private static ResponseStep patchResponse(Step next) { - return new PatchPodResponseStep(next); - } - - private static class PatchPodResponseStep extends ResponseStep { - PatchPodResponseStep(Step next) { - super(next); - } - - @Override - public NextAction onSuccess(Packet packet, CallResponse callResponse) { - DomainPresenceInfo info = packet.getSpi(DomainPresenceInfo.class); - V1Pod pod = callResponse.getResult(); - info.setServerPod(getPodServerName(pod), pod); - return doNext(packet); - } - - @Override - public NextAction onFailure(Packet packet, CallResponse callResponse) { - if (callResponse.getStatusCode() == HTTP_NOT_FOUND) { - return doNext(packet); - } - return super.onFailure(packet, callResponse); - } - } - - /** - * Annotate pod as needing to shut down. - * @param pod Pod - * @param next Next step - * @return Step that will check for existing annotation and add if it is missing - */ - public static Step annotatePodAsNeedingToShutdown(V1Pod pod, String value, Step next) { - return patchPodAnnotation(pod, LabelConstants.TO_BE_SHUTDOWN_LABEL, value, next); - } - - /** - * Check if the pod is already annotated for shut down. - * @param pod Pod - * @return true, if the pod is already annotated. - */ - public static boolean isPodAlreadyAnnotatedForShutdown(V1Pod pod) { - return !Objects.isNull(getPodShutdownAnnotation(pod)); - } - - public static String getPodShutdownAnnotation(V1Pod pod) { - return getPodAnnotation(pod, LabelConstants.TO_BE_SHUTDOWN_LABEL); - } - /** * Get the message from the pod's status. * @param pod pod diff --git a/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java b/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java index 317ae80f4b1..d74ad7117c6 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStep.java @@ -5,7 +5,6 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.time.OffsetDateTime; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -67,9 +66,9 @@ private ShutdownManagedServerStep(Step next, String serverName, V1Pod pod) { /** * Creates asynchronous {@link Step}. * - * @param next Next processing step + * @param next Next processing step * @param serverName name of server - * @param pod server pod + * @param pod server pod * @return asynchronous step */ static Step createShutdownManagedServerStep(Step next, String serverName, V1Pod pod) { @@ -81,15 +80,15 @@ public NextAction apply(Packet packet) { LOGGER.fine(MessageKeys.BEGIN_SERVER_SHUTDOWN_REST, serverName); V1Service service = getDomainPresenceInfo(packet).getServerService(serverName); - String now = OffsetDateTime.now().toString(); - if (service == null || !PodHelper.isReady(pod) || PodHelper.isFailed(pod) || PodHelper.isWaitingToRoll(pod)) { - return doNext(PodHelper.annotatePodAsNeedingToShutdown(pod, now, getNext()), packet); - } - return doNext( + if (service == null) { + return doNext(packet); + } else { + return doNext( Step.chain( - SecretHelper.createAuthorizationSourceStep(), - PodHelper.annotatePodAsNeedingToShutdown(pod, now, - new ShutdownManagedServerWithHttpStep(service, pod, getNext()))), packet); + SecretHelper.createAuthorizationSourceStep(), + new ShutdownManagedServerWithHttpStep(service, pod, getNext())), + packet); + } } static final class ShutdownManagedServerProcessing extends HttpRequestProcessing { diff --git a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java index e79498147a8..84208cff788 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DomainProcessorTest.java @@ -726,7 +726,6 @@ void afterMakeRightAndChangeServerToNever_serverPodsWaitForShutdownWithHttpToCom domainConfigurator.withDefaultServerStartPolicy(ServerStartPolicy.NEVER); DomainStatus status = newInfo.getDomain().getStatus(); defineServerShutdownWithHttpOkResponse(); - makePodsReady(); setAdminServerStatus(status, SUSPENDING_STATE); setManagedServerState(status, SUSPENDING_STATE); diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java index 889a36c5165..99a38ad0cb3 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodPresenceTest.java @@ -517,7 +517,23 @@ void onModifyEventWithEvictedServerPod_cycleServerPod() { assertThat(numPodsDeleted, is(1)); assertThat(createdPodNames, hasItem(SERVER)); } - + + @Test + void onModifyEventWithEvictedServerPod_dontCycleAlreadyEvictedServerPod() { + V1Pod currentPod = withEvictedStatus(createServerPod()); + V1Pod modifiedPod = withEvictedStatus(createServerPod()); + List createdPodNames = new ArrayList<>(); + testSupport.doOnCreate(POD, p -> recordPodCreation((V1Pod) p, createdPodNames)); + + info.setServerPod(SERVER, currentPod); + Watch.Response event = WatchEvent.createModifiedEvent(modifiedPod).toWatchResponse(); + + processor.dispatchPodWatch(event); + + assertThat(numPodsDeleted, is(0)); + assertThat(createdPodNames, not(hasItem(SERVER))); + } + @Test void onModifyEventWithEvictedServerPod_notCycleServerPod_ifConfiguredNotTo() { TuningParametersStub.setParameter("restartEvictedPods", "false"); diff --git a/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java b/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java index bf80866ee9f..c7ae817b4be 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/steps/ShutdownManagedServerStepTest.java @@ -18,9 +18,7 @@ import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Pod; -import io.kubernetes.client.openapi.models.V1PodCondition; import io.kubernetes.client.openapi.models.V1PodSpec; -import io.kubernetes.client.openapi.models.V1PodStatus; import io.kubernetes.client.openapi.models.V1Service; import oracle.kubernetes.operator.DomainProcessorTestSetup; import oracle.kubernetes.operator.KubernetesConstants; @@ -200,13 +198,7 @@ private V1Pod createPod(String serverName) { List env = addShutdownEnvVars(); List containers = addEnvToWLSContainer(env); V1PodSpec podSpec = new V1PodSpec().containers(containers); - return new V1Pod().metadata(createManagedPodMetadata(serverName)).spec(podSpec).status(createPodReadyStatus()); - } - - private V1PodStatus createPodReadyStatus() { - return new V1PodStatus() - .phase("Running") - .addConditionsItem(new V1PodCondition().status("True").type("Ready")); + return new V1Pod().metadata(createManagedPodMetadata(serverName)).spec(podSpec); } @Nonnull From 75eb1fffce59413f9ecc76cc09415b46d547bc84 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 27 Mar 2025 13:03:21 -0400 Subject: [PATCH 299/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8865b62f6ca..96529b1786a 100644 --- a/pom.xml +++ b/pom.xml @@ -733,7 +733,7 @@ 12.1.0 2.0.17 1.5.18 - 4.30.1 + 4.30.2 2.5.2 10.0.2 ${project.basedir}/src-generated-swagger From c73f15f93c86a0f6b2ab98ea5f890889dce3f0ab Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 27 Mar 2025 13:42:33 -0400 Subject: [PATCH 300/356] Prepare for WKO 4.2.16 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 36292a9904a..26aaf21e05d 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index a99ec674ccd..19a5cb924ca 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index c2febf60f68..5bc42220a25 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 10586b0a3d4..f341f48d43a 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.16-SNAPSHOT + 4.2.16 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 6f017aa7ece..3f086f69fe4 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index fc80bf3d0e4..108622f9219 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.16-SNAPSHOT + 4.2.16 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index f3d036add6b..8016c832606 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 96529b1786a..e2ac6220629 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 843fce4cda9..faa6a728fdc 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16-SNAPSHOT + 4.2.16 operator-swagger From 2f457d2b597c33ab83b81dbad3e7f9fef2b9ba97 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 27 Mar 2025 15:10:23 -0400 Subject: [PATCH 301/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 26aaf21e05d..4313de8301a 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 19a5cb924ca..c72fb68c6df 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 5bc42220a25..9abdedb64ec 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index f341f48d43a..9d814500a13 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.16 + 4.2.17-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 3f086f69fe4..7e67857b95e 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 108622f9219..e8a32cfb433 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.16 + 4.2.17-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 8016c832606..7cc2fa2542d 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index e2ac6220629..50e86ffd516 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index faa6a728fdc..626c6462120 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.16 + 4.2.17-SNAPSHOT operator-swagger From 7688150a36266e2533bca816bc39bf09906203eb Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 28 Mar 2025 17:34:43 +0000 Subject: [PATCH 302/356] Add Openshift cluster Jenkinsfile to run the Openshift certification tests --- Jenkinsfile.openshift | 512 ++++++++++++++++++ .../extensions/InitializationTasks.java | 2 +- 2 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 Jenkinsfile.openshift diff --git a/Jenkinsfile.openshift b/Jenkinsfile.openshift new file mode 100644 index 00000000000..41c1d9fd661 --- /dev/null +++ b/Jenkinsfile.openshift @@ -0,0 +1,512 @@ + +// Copyright (c) 2025, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// + +CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=wko-okd-wls-srg + H 2 * * * % MAVEN_PROFILE_NAME=wko-okd-wls-mrg + H 3 * * * % MAVEN_PROFILE_NAME=wko-okd-fmw-cert''' + +pipeline { + agent { label 'large' } + options { + timeout(time: 1800, unit: 'MINUTES') + disableConcurrentBuilds() + } + triggers { + // timer trigger for "nightly build" + parameterizedCron(env.JOB_NAME == 'wko-release42-nightly-okd' ? + CRON_SETTINGS : '') + } + + tools { + maven 'maven-3.8.7' + jdk 'jdk21' + } + + environment { + ocir_host = "${env.WKT_OCIR_HOST}" + wko_tenancy = "${env.WKT_TENANCY}" + ocir_creds = 'wkt-ocir-creds' + + outdir = "${WORKSPACE}/staging" + result_root = "${outdir}/wl_k8s_test_results" + pv_root = "/export/wls" + nfs_server = "${env.OKD_NFS_SERVER}" + okd_ip = "${env.OKD_IP}" + + okdkub = 'openshift-kubeconfig' + + start_time = sh(script: 'date +"%Y-%m-%d %H:%M:%S"', returnStdout: true).trim() + wle_download_url="https://github.com/oracle/weblogic-logging-exporter/releases/latest" + } + + parameters { + string(name: 'BRANCH', + description: 'The branch for weblogic-kubernetes-operator project', + defaultValue: "release/4.2" + ) + + choice(name: 'MAVEN_PROFILE_NAME', + description: 'Profile to use in mvn command to run the tests. Possible values are integration-tests. Refer to weblogic-kubernetes-operator/integration-tests/pom.xml on the branch.', + choices: [ + 'wko-okd-wls-srg', + 'wko-okd-wls-mrg', + 'wko-okd-fmw-cert', + 'integration-tests' + ] + ) + + string(name: 'IT_TEST', + description: 'Comma separated list of individual It test classes to be run e.g., ItParameterizedDomain, ItMiiUpdateDomainConfig, ItMiiDynamicUpdate*, ItMiiMultiMode', + defaultValue: '' + ) + + string(name: 'OPERATOR_LOG_LEVEL', + description: 'The default log level is not set', + defaultValue: '' + ) + + string(name: 'KUBECTL_VERSION', + description: 'kubectl version', + defaultValue: '1.26.2' + ) + + string(name: 'HELM_VERSION', + description: 'Helm version', + defaultValue: '3.11.2' + ) + + choice(name: 'ISTIO_VERSION', + description: 'Istio version', + choices: [ + '1.17.2', + '1.16.1', + '1.13.2', + '1.12.6', + '1.11.1', + '1.10.4', + '1.9.9' + ] + ) + + booleanParam(name: 'PARALLEL_RUN', + description: 'Runs tests in parallel. Default is false, test classes run in parallel.', + defaultValue: false + ) + string(name: 'NUMBER_OF_THREADS', + description: 'Number of threads to run the classes in parallel, default is 2.', + defaultValue: "3" + ) + string(name: 'WDT_DOWNLOAD_URL', + description: 'URL to download WDT.', + defaultValue: 'https://github.com/oracle/weblogic-deploy-tooling/releases/latest' + ) + string(name: 'WIT_DOWNLOAD_URL', + description: 'URL to download WIT.', + defaultValue: 'https://github.com/oracle/weblogic-image-tool/releases/latest' + ) + string(name: 'REPO_REGISTRY', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + + choice(name: 'BASE_IMAGES_REPO', + choices: ["${env.WKT_OCIR_HOST}", 'container-registry.oracle.com'], + description: 'Repository to pull the base images. Make sure to modify the image names if you are modifying this parameter value.' + ) + string(name: 'TEST_IMAGES_REPO', + description: '', + defaultValue: "${env.WKT_OCIR_HOST}" + ) + string(name: 'WEBLOGIC_IMAGE_NAME', + description: 'WebLogic base image name. Default is the image name in OCIR. Use middleware/weblogic for OCR.', + defaultValue: 'test-images/weblogic' + ) + string(name: 'WEBLOGIC_IMAGE_TAG', + description: '12.2.1.3 (12.2.1.3-ol7) , 12.2.1.3-dev (12.2.1.3-dev-ol7), 12.2.1.3-ol8, 12.2.1.3-dev-ol8, 12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '12.2.1.4' + ) + string(name: 'FMWINFRA_IMAGE_NAME', + description: 'FWM Infra image name. Default is the image name in OCIR. Use middleware/fmw-infrastructure for OCR.', + defaultValue: 'test-images/fmw-infrastructure' + ) + string(name: 'FMWINFRA_IMAGE_TAG', + description: 'FWM Infra image tag', + defaultValue: '12.2.1.4' + ) + string(name: 'DB_IMAGE_NAME', + description: 'Oracle DB image name. Default is the image name in OCIR, use database/enterprise for OCR.', + defaultValue: 'test-images/database/enterprise' + ) + string(name: 'DB_IMAGE_TAG', + description: 'Oracle DB image tag', + defaultValue: '12.2.0.1-slim' + ) + string(name: 'MONITORING_EXPORTER_BRANCH', + description: '', + defaultValue: 'main' + ) + string(name: 'MONITORING_EXPORTER_WEBAPP_VERSION', + description: '', + defaultValue: '2.1.3' + ) + string(name: 'PROMETHEUS_CHART_VERSION', + description: '', + defaultValue: '17.0.0' + ) + string(name: 'GRAFANA_CHART_VERSION', + description: '', + defaultValue: '6.44.11' + ) + booleanParam(name: 'COLLECT_LOGS_ON_SUCCESS', + description: 'Collect logs for successful runs. Default is false.', + defaultValue: false + ) + string(name: 'REMOTECONSOLE_VERSION', + description: 'RemoteConsole version.', + defaultValue: '2.4.7' + ) + } + + stages { + stage('Filter unwanted branches') { + stages { + stage('Workaround JENKINS-41929 Parameters bug') { + steps { + echo 'Initialize parameters as environment variables due to https://issues.jenkins-ci.org/browse/JENKINS-41929' + evaluate """${def script = ""; params.each { k, v -> script += "env.${k} = '''${v}'''\n" }; return script}""" + } + } + stage ('Echo environment') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + env|sort + java -version + mvn --version + python --version + docker version + ulimit -a + ulimit -aH + ''' + } + } + stage('Make Workspace bin directory') { + steps { + sh "mkdir -m777 -p ${WORKSPACE}/bin" + } + } + + stage('Build WebLogic Kubernetes Operator') { + steps { + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + sh "mvn -DtrimStackTrace=false clean install" + } + } + } + + stage('Install Helm') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + echo "$(pwd)" + + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=helm/helm-v${HELM_VERSION}.tar.gz --file=helm.tar.gz \ + --auth=instance_principal + tar zxf helm.tar.gz + mv linux-amd64/helm ${WORKSPACE}/bin/helm + rm -rf linux-amd64 + helm version + ''' + } + } + + stage('Run Helm installation tests') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + sh 'export PATH=${runtime_path} && mvn -pl kubernetes -P helm-installation-test verify' + } + } + } + + stage ('Install kubectl') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + echo "$(pwd)" + oci os object get --namespace=${wko_tenancy} --bucket-name=wko-system-test-files \ + --name=kubectl/kubectl-v${KUBECTL_VERSION} --file=${WORKSPACE}/bin/kubectl \ + --auth=instance_principal + chmod +x ${WORKSPACE}/bin/kubectl + kubectl version --client=true + ''' + } + } + + stage ('Install OpenShift CLI oc') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + sh ''' + export PATH=${runtime_path} + wget -N https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest/linux/oc.tar.gz + tar xvf oc.tar.gz + mv oc ${WORKSPACE}/bin/ + echo "PATH: " $PATH + oc version + ''' + } + } + + stage('Preparing Integration Test Environment') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + steps { + withCredentials([file(credentialsId: "${okdkub}", variable: 'KUBECONFIG_FILE') + ]) { + + sh ''' + export NO_PROXY=${OKD_IP},$NO_PROXY,nip.io + export no_proxy=${OKD_IP},$no_proxy,nip.io + + echo "NO_PROXY:" $NO_PROXY + echo "no_proxy:" $no_proxy + + export PATH=${runtime_path} + export KUBECONFIG=${KUBECONFIG_FILE} + cat $KUBECONFIG + + KUBERNETES_CLI=${KUBERNETES_CLI:-kubectl} + + echo "Checking nodes" + ${KUBERNETES_CLI} get nodes -o wide + + mkdir -m777 -p ${result_root} + echo "Results will be in ${result_root}" + + echo "cleaning up k8s artifacts" + ${KUBERNETES_CLI} delete crd $(${KUBERNETES_CLI} get crd | grep weblogic) || true + + ${KUBERNETES_CLI} get ns --no-headers | awk '$1 ~ /^ns-/{print $1}' | xargs ${KUBERNETES_CLI} delete ns || true + ${KUBERNETES_CLI} get ns --no-headers | awk '/weblogic/{print $1}' | xargs ${KUBERNETES_CLI} delete ns || true + ${KUBERNETES_CLI} get ns --no-headers | awk '/test-/{print $1}' | xargs ${KUBERNETES_CLI} delete ns || true + ${KUBERNETES_CLI} delete pv domain1-weblogic-sample-pv --wait=false || true + ${KUBERNETES_CLI} delete pv domain2-weblogic-sample-pv --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testalertmanager --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafana --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheus --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testalertmanagertest1 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafanatest1 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheustest1 --wait=false || true + + ${KUBERNETES_CLI} delete pv pv-testalertmanagertest2 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafanatest2 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheustest2 --wait=false || true + + ${KUBERNETES_CLI} delete pv pv-testalertmanagertest3 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testgrafanatest3 --wait=false || true + ${KUBERNETES_CLI} delete pv pv-testprometheustest3 --wait=false || true + + ${KUBERNETES_CLI} get ingressroutes -A --no-headers | awk '/tdlbs-/{print $2}' | xargs ${KUBERNETES_CLI} delete ingressroute || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/ns-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/appscode/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/nginx-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + ${KUBERNETES_CLI} get clusterroles --no-headers | awk '/traefik-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterroles || true + + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/ns-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/appscode/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/nginx-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + ${KUBERNETES_CLI} get clusterrolebindings --no-headers | awk '/traefik-/{print $1}' | xargs ${KUBERNETES_CLI} delete clusterrolebindings || true + + echo "pv_root: " ${pv_root} + + ''' + } + } + } + + stage('Run integration tests') { + environment { + runtime_path = "${WORKSPACE}/bin:${PATH}" + } + + steps { + script { + currentBuild.description = "${GIT_BRANCH} ${MAVEN_PROFILE_NAME}" + def res = 0 + res = sh(script: ''' + if [ -z "${IT_TEST}" ] && [ "${MAVEN_PROFILE_NAME}" = "integration-tests" ]; then + echo 'ERROR: All tests cannot be run with integration-tests profile' + exit 1 + fi + ''', returnStatus: true) + if (res != 0 ) { + currentBuild.result = 'ABORTED' + error('Profile/ItTests Validation Failed') + } + } + + sh ''' + + export PATH=${runtime_path} + export OKD=true + export NFS_SERVER=${OKD_NFS_SERVER} + echo "NFS_SERVER is: " $NFS_SERVER + export pv_root="/export/wls" + mkdir -m777 -p "${WORKSPACE}/.mvn" + touch ${WORKSPACE}/.mvn/maven.config + + if [ -n "${IT_TEST}" ]; then + echo 'Overriding MAVEN_PROFILE_NAME to integration-test when running individual test(s)' + MAVEN_PROFILE_NAME="integration-tests" + echo "-Dit.test=\"${IT_TEST}\"" >> ${WORKSPACE}/.mvn/maven.config + fi + + echo "MAVEN_PROFILE_NAME:" $MAVEN_PROFILE_NAME + echo "PARALLEL_RUN:" $PARALLEL_RUN + echo "-Dwko.it.wle.download.url=\"${wle_download_url}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.result.root=\"${result_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.pv.root=\"${pv_root}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.k8s.nodeport.host=\"${K8S_NODEPORT_HOST}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.nfs.server=\"${NFS_SERVER}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.istio.version=\"${ISTIO_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DPARALLEL_CLASSES=\"${PARALLEL_RUN}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DNUMBER_OF_THREADS=\"${NUMBER_OF_THREADS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wdt.download.url=\"${WDT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.wit.download.url=\"${WIT_DOWNLOAD_URL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.repo=\"${BASE_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.base.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.repo=\"${TEST_IMAGES_REPO}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.test.images.tenancy=\"${wko_tenancy}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.name=\"${WEBLOGIC_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.weblogic.image.tag=\"${WEBLOGIC_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.name=\"${FMWINFRA_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.fmwinfra.image.tag=\"${FMWINFRA_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.name=\"${DB_IMAGE_NAME}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.db.image.tag=\"${DB_IMAGE_TAG}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.branch=\"${MONITORING_EXPORTER_BRANCH}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.monitoring.exporter.webapp.version=\"${MONITORING_EXPORTER_WEBAPP_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.prometheus.chart.version=\"${PROMETHEUS_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.grafana.chart.version=\"${GRAFANA_CHART_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.collect.logs.on.success=\"${COLLECT_LOGS_ON_SUCCESS}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.remoteconsole.version=\"${REMOTECONSOLE_VERSION}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-DOPERATOR_LOG_LEVEL=\"${OPERATOR_LOG_LEVEL}\"" >> ${WORKSPACE}/.mvn/maven.config + echo "-Dwko.it.install.weblogic=\"true\"" >> ${WORKSPACE}/.mvn/maven.config + + echo "${WORKSPACE}/.mvn/maven.config contents:" + cat "${WORKSPACE}/.mvn/maven.config" + cp "${WORKSPACE}/.mvn/maven.config" "${result_root}" + + ''' + withMaven(globalMavenSettingsConfig: 'wkt-maven-settings-xml', publisherStrategy: 'EXPLICIT') { + withCredentials([ + usernamePassword(credentialsId: "${ocir_creds}", usernameVariable: 'OCIR_USER', passwordVariable: 'OCIR_PASS'), + file(credentialsId: "${okdkub}", variable: 'KUBECONFIG_FILE') + ]) { + sh ''' + + export NO_PROXY=${OKD_IP},$NO_PROXY + export no_proxy=${OKD_IP},$no_proxy + + + echo "NO_PROXY:" $NO_PROXY + echo "no_proxy:" $no_proxy + + export PATH=${runtime_path} + export OKD="true" + export NFS_SERVER=${OKD_NFS_SERVER} + echo "NFS_SERVER is: " $NFS_SERVER + export pv_root="/export/wls" + + export KUBECONFIG=${KUBECONFIG_FILE} + cat $KUBECONFIG + echo "Checking nodes" + KUBERNETES_CLI=${KUBERNETES_CLI:-kubectl} + ${KUBERNETES_CLI} get nodes -o wide + + echo "K8S_NODEPORT_HOST:" $K8S_NODEPORT_HOST + + export BASE_IMAGES_REPO_USERNAME="${OCIR_USER}" + export BASE_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export BASE_IMAGES_REPO_EMAIL="noreply@oracle.com" + export TEST_IMAGES_REPO_USERNAME="${OCIR_USER}" + export TEST_IMAGES_REPO_PASSWORD="${OCIR_PASS}" + export TEST_IMAGES_REPO_EMAIL="noreply@oracle.com" + + + if [[ -n "${IT_TEST}" && "${IT_TEST}" != "**/It*" ]]; then + echo 'Overriding MAVEN_PROFILE_NAME to integration-test when running individual test(s)' + export MAVEN_PROFILE_NAME="integration-tests" + fi + echo "MAVEN_PROFILE_NAME:" $MAVEN_PROFILE_NAME + if ! mvn -pl integration-tests -P ${MAVEN_PROFILE_NAME} verify 2>&1 | tee "${result_root}/okdtest.log"; then + echo "integration-tests failed" + exit 1 + fi + ''' + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + + mkdir -m777 -p ${result_root}/kubelogs + mkdir -m777 -p "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + sudo mv -f ${result_root}/* "${WORKSPACE}/logdir/${BUILD_TAG}/wl_k8s_test_results" + + ''' + + archiveArtifacts(artifacts: + "logdir/${BUILD_TAG}/wl_k8s_test_results/diagnostics/**/*,logdir/${BUILD_TAG}/wl_k8s_test_results/workdir/liftandshiftworkdir/**/*,integration-tests/target/failsafe-reports/*.xml") + junit(testResults: 'integration-tests/target/failsafe-reports/*.xml', allowEmptyResults: true) + } + } + } + } + post { + always { + sh ''' + export PATH="${WORKSPACE}/bin:${PATH}" + + rm -rf ${WORKSPACE}/.mvn + + + ''' + } + } + } + + stage ('Sync') { + when { + anyOf { + branch 'release/4.2' + } + anyOf { + not { triggeredBy 'TimerTrigger' } + tag 'v*' + } + } + steps { + build job: "wkt-sync", parameters: [ string(name: 'REPOSITORY', value: 'weblogic-kubernetes-operator') ] + } + } + } +} diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index 59d8ccd1cc3..e3017335e90 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -332,7 +332,7 @@ public void beforeAll(ExtensionContext context) { logger.info("Installing istio before any test suites are run"); installIstio(); } - if (INSTALL_WEBLOGIC && !OKD && !CRIO && !ARM && !OKE_CLUSTER) { + if (INSTALL_WEBLOGIC && !CRIO && !ARM && !OKE_CLUSTER) { installOnPremWebLogic(); } } finally { From 34870bf6aa91bfaf4a578deb801d2ba9036637b0 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Fri, 28 Mar 2025 17:35:02 +0000 Subject: [PATCH 303/356] replace the string with proper managed server port number --- .../test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java index a360c3e9dae..25701d4488f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/IstioUtils.java @@ -555,6 +555,7 @@ public static int createIstioService( templateMap.put("DUID", domainUid); templateMap.put("ADMIN_SERVICE",adminServerPodName); templateMap.put("CLUSTER_SERVICE", clusterService); + templateMap.put("MANAGED_SERVER_PORT", "8001"); Path srcHttpFile = Paths.get(RESOURCE_DIR, "istio", "istio-http-template.yaml"); Path targetHttpFile = assertDoesNotThrow( From 5bf77ae674af220ec80e43cc0141c4d61474b4a5 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 2 Apr 2025 09:43:28 -0400 Subject: [PATCH 304/356] Dependency updates --- operator-build-maven-plugin/pom.xml | 4 ++-- pom.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index e8a32cfb433..44ffd52cd24 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -1,4 +1,4 @@ - org.ow2.asm asm - 9.7.1 + 9.8 test diff --git a/pom.xml b/pom.xml index 50e86ffd516..6ab9556d987 100644 --- a/pom.xml +++ b/pom.xml @@ -680,15 +680,15 @@ 3.4.2 3.3.1 3.21.0 - 3.5.2 + 3.5.3 3.6.0 3.1.1 3.11.2 - 3.5.2 + 3.5.3 3.8.1 3.6.0 3.5.0 - 10.21.4 + 10.23.0 1.0 3.6.0 3.2.7 From 9fdf7a4333038492f3ce73827d8d74445f263fff Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 1 Apr 2025 18:20:05 -0400 Subject: [PATCH 305/356] Support serverPod.automountServiceAccountToken --- documentation/domains/Cluster.json | 4 ++++ documentation/domains/Cluster.md | 1 + documentation/domains/Domain.json | 4 ++++ documentation/domains/Domain.md | 1 + .../oracle/weblogic/domain/ServerPod.java | 24 ++++++++++++++++++- kubernetes/crd/cluster-crd.yaml | 7 +++++- kubernetes/crd/domain-crd.yaml | 17 ++++++++++++- .../operator/helpers/BasePodStepContext.java | 3 ++- .../operator/helpers/JobStepContext.java | 1 + .../processing/EffectiveServerSpec.java | 4 +++- .../domain/model/BaseConfiguration.java | 4 ++++ .../model/EffectiveServerSpecCommonImpl.java | 5 ++++ .../weblogic/domain/model/ServerPod.java | 18 ++++++++++++++ 13 files changed, 88 insertions(+), 5 deletions(-) diff --git a/documentation/domains/Cluster.json b/documentation/domains/Cluster.json index 52f5e27da5e..d37f55001e5 100644 --- a/documentation/domains/Cluster.json +++ b/documentation/domains/Cluster.json @@ -234,6 +234,10 @@ "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration" } }, + "automountServiceAccountToken": { + "description": "Indicates whether a service account token should be automatically mounted on the pod. Defaults to true if not set. See `kubectl explain pods.spec.automountServiceAccountToken`.", + "type": "boolean" + }, "readinessProbe": { "description": "Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe" diff --git a/documentation/domains/Cluster.md b/documentation/domains/Cluster.md index 480816a6e54..8c468ff1d04 100644 --- a/documentation/domains/Cluster.md +++ b/documentation/domains/Cluster.md @@ -55,6 +55,7 @@ The specification of the operation of the WebLogic cluster. Required. | --- | --- | --- | | `affinity` | [Affinity](k8s1.28.2.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/. See `kubectl explain pods.spec.affinity`. | | `annotations` | Map | The annotations to be added to generated resources. | +| `automountServiceAccountToken` | Boolean | Indicates whether a service account token should be automatically mounted on the pod. Defaults to true if not set. See `kubectl explain pods.spec.automountServiceAccountToken`. | | `containers` | Array of [Container](k8s1.28.2.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. | | `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. If no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. | diff --git a/documentation/domains/Domain.json b/documentation/domains/Domain.json index 3c5eec032ee..9047d93540f 100644 --- a/documentation/domains/Domain.json +++ b/documentation/domains/Domain.json @@ -1107,6 +1107,10 @@ "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration" } }, + "automountServiceAccountToken": { + "description": "Indicates whether a service account token should be automatically mounted on the pod. Defaults to true if not set. See `kubectl explain pods.spec.automountServiceAccountToken`.", + "type": "boolean" + }, "readinessProbe": { "description": "Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will create an HTTP probe accessing the /weblogic/ready path. If an HTTP probe is specified then the operator will fill in `path`, `port`, and `scheme`, if they are missing. The operator will also fill in any missing tuning-related fields if they are unspecified. Tuning-related fields will be inherited from the domain and cluster scopes unless a more specific scope defines a different action, such as a different HTTP path to access.", "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.28.2/_definitions.json#/definitions/io.k8s.api.core.v1.Probe" diff --git a/documentation/domains/Domain.md b/documentation/domains/Domain.md index 1a8130ce2e1..b931258e0a1 100644 --- a/documentation/domains/Domain.md +++ b/documentation/domains/Domain.md @@ -156,6 +156,7 @@ The current status of the operation of the WebLogic domain. Updated automaticall | --- | --- | --- | | `affinity` | [Affinity](k8s1.28.2.md#affinity) | The Pod's scheduling constraints. More info: https://oracle.github.io/weblogic-kubernetes-operator/faq/node-heating/. See `kubectl explain pods.spec.affinity`. | | `annotations` | Map | The annotations to be added to generated resources. | +| `automountServiceAccountToken` | Boolean | Indicates whether a service account token should be automatically mounted on the pod. Defaults to true if not set. See `kubectl explain pods.spec.automountServiceAccountToken`. | | `containers` | Array of [Container](k8s1.28.2.md#container) | Additional containers to be included in the server Pod. See `kubectl explain pods.spec.containers`. | | `containerSecurityContext` | [Security Context](k8s1.28.2.md#security-context) | Container-level security attributes. Will override any matching Pod-level attributes. See `kubectl explain pods.spec.containers.securityContext`. If no value is specified for this field, the operator will use default content for container-level `securityContext`. More info: https://oracle.github.io/weblogic-kubernetes-operator/security/domain-security/pod-and-container/. | | `env` | Array of [Env Var](k8s1.28.2.md#env-var) | A list of environment variables to set in the container running a WebLogic Server instance. More info: https://oracle.github.io/weblogic-kubernetes-operator/userguide/managing-domains/domain-resource/#jvm-memory-and-java-option-environment-variables. See `kubectl explain pods.spec.containers.env`. | diff --git a/integration-tests/src/test/java/oracle/weblogic/domain/ServerPod.java b/integration-tests/src/test/java/oracle/weblogic/domain/ServerPod.java index 69d46ed89ec..961b4cd4310 100644 --- a/integration-tests/src/test/java/oracle/weblogic/domain/ServerPod.java +++ b/integration-tests/src/test/java/oracle/weblogic/domain/ServerPod.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.domain; @@ -96,6 +96,11 @@ public class ServerPod { + "ServiceAccount will be used. The ServiceAccount has to exist at the time the pod is created.") private String serviceAccountName; + @ApiModelProperty( + "Indicates whether a service account token should be automatically mounted on the pod. " + + "Defaults to true if not set. See `kubectl explain pods.spec.automountServiceAccountToken`.") + private Boolean automountServiceAccountToken = null; + @ApiModelProperty("Memory and CPU minimum requirements and limits for the server.") private V1ResourceRequirements resources; @@ -462,6 +467,23 @@ public void setServiceAccountName(String serviceAccountName) { this.serviceAccountName = serviceAccountName; } + public ServerPod automountServiceAccountToken(Boolean automountServiceAccountToken) { + this.automountServiceAccountToken = automountServiceAccountToken; + return this; + } + + public Boolean automountServiceAccountToken() { + return automountServiceAccountToken; + } + + public Boolean getAutomountServiceAccountToken() { + return automountServiceAccountToken; + } + + public void setAutomountServiceAccountToken(Boolean automountServiceAccountToken) { + this.automountServiceAccountToken = automountServiceAccountToken; + } + public ServerPod resources(V1ResourceRequirements resources) { this.resources = resources; return this; diff --git a/kubernetes/crd/cluster-crd.yaml b/kubernetes/crd/cluster-crd.yaml index 7684b9f6d1e..374e68aed86 100644 --- a/kubernetes/crd/cluster-crd.yaml +++ b/kubernetes/crd/cluster-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 34ed6e0ff57580da665db440dce607569f175d0d93934c0e31b2ab26e7ede28d + weblogic.sha256: 251af6bc71a6c64c9f40bf8b60f5eee1a82dbf32a3439f7fe035c0baa89280bd name: clusters.weblogic.oracle spec: group: weblogic.oracle @@ -329,6 +329,11 @@ spec: operator: type: string type: array + automountServiceAccountToken: + description: Indicates whether a service account token should + be automatically mounted on the pod. Defaults to true if not + set. See `kubectl explain pods.spec.automountServiceAccountToken`. + type: boolean readinessProbe: description: Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will diff --git a/kubernetes/crd/domain-crd.yaml b/kubernetes/crd/domain-crd.yaml index c0963bfc08a..cb69327ca2a 100644 --- a/kubernetes/crd/domain-crd.yaml +++ b/kubernetes/crd/domain-crd.yaml @@ -5,7 +5,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - weblogic.sha256: 3c9cb1077e51677d24c80535ad61c7b524c415e44dda7eda48e9e344f4bb3809 + weblogic.sha256: d5781dca31795f836ae89e7139d88fce344b88395f61659d8c7c912467c70a98 name: domains.weblogic.oracle spec: group: weblogic.oracle @@ -1433,6 +1433,11 @@ spec: operator: type: string type: array + automountServiceAccountToken: + description: Indicates whether a service account token should + be automatically mounted on the pod. Defaults to true if + not set. See `kubectl explain pods.spec.automountServiceAccountToken`. + type: boolean readinessProbe: description: Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator @@ -5216,6 +5221,11 @@ spec: operator: type: string type: array + automountServiceAccountToken: + description: Indicates whether a service account token should + be automatically mounted on the pod. Defaults to true if not + set. See `kubectl explain pods.spec.automountServiceAccountToken`. + type: boolean readinessProbe: description: Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the operator will @@ -8107,6 +8117,11 @@ spec: type: string operator: type: string + automountServiceAccountToken: + description: Indicates whether a service account token should + be automatically mounted on the pod. Defaults to true + if not set. See `kubectl explain pods.spec.automountServiceAccountToken`. + type: boolean readinessProbe: description: Settings for the readiness probe associated with a WebLogic Server instance. If not specified, the diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java index 60aaaf84918..fe065b4da1e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2024, Oracle and/or its affiliates. +// Copyright (c) 2019, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -228,6 +228,7 @@ protected V1PodSpec createPodSpec() { .topologySpreadConstraints(getTopologySpreadConstraints()) .nodeSelector(getServerSpec().getNodeSelectors()) .serviceAccountName(getServerSpec().getServiceAccountName()) + .automountServiceAccountToken(getServerSpec().getAutomountServiceAccountToken()) .nodeName(getServerSpec().getNodeName()) .schedulerName(getServerSpec().getSchedulerName()) .priorityClassName(getServerSpec().getPriorityClassName()) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 68cdef91df6..813cf2e16e1 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -584,6 +584,7 @@ protected V1PodSpec createPodSpec() { .activeDeadlineSeconds(getActiveDeadlineSeconds()) .restartPolicy("Never") .serviceAccountName(info.getDomain().getSpec().getServiceAccountName()) + .automountServiceAccountToken(info.getDomain().getSpec().getAutomountServiceAccountToken()) .addVolumesItem(new V1Volume().name(SECRETS_VOLUME).secret(getSecretsVolume())) .addVolumesItem( new V1Volume().name(SCRIPTS_VOLUME).configMap(getConfigMapVolumeSource())) diff --git a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java index 405fbbe5d2e..6eca3d9286f 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java +++ b/operator/src/main/java/oracle/kubernetes/operator/processing/EffectiveServerSpec.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2024, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.processing; @@ -162,6 +162,8 @@ public interface EffectiveServerSpec { String getServiceAccountName(); + Boolean getAutomountServiceAccountToken(); + String getSchedulerName(); List getTolerations(); diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java index 237ca52dd88..07ccec6ae18 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/BaseConfiguration.java @@ -239,6 +239,10 @@ public String getServiceAccountName() { return serverPod.getServiceAccountName(); } + public Boolean getAutomountServiceAccountToken() { + return serverPod.getAutomountServiceAccountToken(); + } + public void setNodeName(String nodeName) { serverPod.setNodeName(nodeName); } diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java index d6e6b662451..94a97bfd098 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/EffectiveServerSpecCommonImpl.java @@ -220,6 +220,11 @@ public String getServiceAccountName() { return server.getServiceAccountName(); } + @Override + public Boolean getAutomountServiceAccountToken() { + return server.getAutomountServiceAccountToken(); + } + @Override public String getSchedulerName() { return server.getSchedulerName(); diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java index 174ecb0b5b0..1c79075a897 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/ServerPod.java @@ -180,6 +180,10 @@ class ServerPod extends KubernetesResource { + "See `kubectl explain pods.spec.serviceAccountName`.") private String serviceAccountName = null; + @Description("Indicates whether a service account token should be automatically mounted on the pod. " + + "Defaults to true if not set. See `kubectl explain pods.spec.automountServiceAccountToken`.") + private Boolean automountServiceAccountToken = null; + @Description("HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file " + "if specified. This is only valid for non-hostNetwork pods.") private List hostAliases = new ArrayList<>(); @@ -576,6 +580,9 @@ void fillInFrom(ServerPod serverPod1) { if (serviceAccountName == null) { serviceAccountName = serverPod1.serviceAccountName; } + if (automountServiceAccountToken == null) { + automountServiceAccountToken = serverPod1.automountServiceAccountToken; + } if (schedulerName == null) { schedulerName = serverPod1.schedulerName; } @@ -869,6 +876,14 @@ void setServiceAccountName(String serviceAccountName) { this.serviceAccountName = serviceAccountName; } + Boolean getAutomountServiceAccountToken() { + return automountServiceAccountToken; + } + + void setAutomountServiceAccountToken(Boolean automountServiceAccountToken) { + this.automountServiceAccountToken = automountServiceAccountToken; + } + List getTolerations() { return tolerations; } @@ -922,6 +937,7 @@ public String toString() { .append("tolerations", tolerations) .append("hostAliases", hostAliases) .append("serviceAccountName", serviceAccountName) + .append("automountServiceAccountToken", automountServiceAccountToken) .toString(); } @@ -970,6 +986,7 @@ public boolean equals(Object o) { .append(tolerations, that.tolerations) .append(hostAliases, that.hostAliases) .append(serviceAccountName, that.serviceAccountName) + .append(automountServiceAccountToken, that.automountServiceAccountToken) .isEquals(); } @@ -1002,6 +1019,7 @@ public int hashCode() { .append(tolerations) .append(hostAliases) .append(serviceAccountName) + .append(automountServiceAccountToken) .toHashCode(); } } From 946ab68063e670d221853d2dcec50b8481a4973d Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 2 Apr 2025 21:28:42 +0000 Subject: [PATCH 306/356] updated oke run with k8s 1.32.1 --- Jenkinsfile.oke | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 948728c1482..747ba8f1154 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -83,14 +83,14 @@ pipeline { string(name: 'OKE_KUBE_VERSION', description: 'kube version for oke cluster', - defaultValue: '1.31.1' + defaultValue: '1.32.1' ) string(name: 'IMAGE_ID', description: 'oci image id for node pool, find image OCID for your region from https://docs.oracle.com/iaas/images/', //defaultValue OKE1.26.2: 'ocid1.image.oc1.phx.aaaaaaaaaizmtmozeudeeuq7o5ir7dkl2bkxbbb3tgomshqbqn6jpomrsjza' //1.27.2 oke defaultValue: 'ocid1.image.oc1.phx.aaaaaaaaypr5r5drojwytghw6e6mvpjsscrnkuwtmqlmvmix7kjb2zcnc7wa' //1.29.1 'ocid1.image.oc1.phx.aaaaaaaa22u45gr3ikxnc6rius2qil2kz5k3e7p476c4usr6qnvql4l5dxea' - defaultValue: 'ocid1.image.oc1.phx.aaaaaaaatkz7laillswel25edlbmq67ino2y2pv6xhp2lml2vzha44ao5gtq' + defaultValue: 'ocid1.image.oc1.phx.aaaaaaaak36p3gcd7m5fypfs5j5flxaqxykon4dkjqd3j45zkq57cq52ovoq' ) string(name: 'KUBECTL_VERSION', From 230bec81c284fb6f8d27fbe0a31d518ff32c9ff8 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Wed, 2 Apr 2025 21:28:58 +0000 Subject: [PATCH 307/356] Fixed wdt url download issue in OKE run - OWLS-127270 --- .../kubernetes/ItSystemResOverrides.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java index a3a53cee942..7f3b9aff626 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItSystemResOverrides.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -72,10 +72,12 @@ import static oracle.weblogic.kubernetes.TestConstants.TRAEFIK_INGRESS_HTTP_HOSTPORT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG_DEFAULT; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_SHIPHOME; import static oracle.weblogic.kubernetes.actions.ActionConstants.APP_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.DOWNLOAD_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT; import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_DOWNLOAD_URL; import static oracle.weblogic.kubernetes.actions.TestActions.createSecret; import static oracle.weblogic.kubernetes.actions.TestActions.getNextIntrospectVersion; @@ -84,6 +86,7 @@ import static oracle.weblogic.kubernetes.actions.TestActions.shutdownDomain; import static oracle.weblogic.kubernetes.actions.TestActions.startDomain; import static oracle.weblogic.kubernetes.actions.impl.Domain.patchDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.impl.primitive.Command.defaultCommandParams; import static oracle.weblogic.kubernetes.assertions.TestAssertions.podStateNotChanged; import static oracle.weblogic.kubernetes.assertions.TestAssertions.secretExists; import static oracle.weblogic.kubernetes.utils.ApplicationUtils.verifyAdminServerRESTAccess; @@ -95,6 +98,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.createIngressHostRouting; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.exeAppInServerPod; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getActualLocationIfNeeded; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getDateAndTimeStamp; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getHostAndPort; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; @@ -202,6 +206,9 @@ public void initAll(@Namespaces(2) List namespaces) throws IOException { // create pull secrets for WebLogic image when running in non Kind Kubernetes cluster // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); + if (OKE_CLUSTER) { + installOnPremWebLogic(); + } //create and start WebLogic domain createDomain(); @@ -682,11 +689,13 @@ public static File createWdtPropertyFile(String wlsModelFilePrefix, String nodeP } private static void downloadAndInstallWDT() throws IOException { - String wdtUrl = WDT_DOWNLOAD_URL + "/download/weblogic-deploy.zip"; + + String wdtUrl = getActualLocationIfNeeded(WDT_DOWNLOAD_URL, WDT); + Path destLocation = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy.zip"); encryptModelScript = Path.of(DOWNLOAD_DIR, "wdt", "weblogic-deploy", "bin", "encryptModel.sh"); if (!Files.exists(destLocation) && !Files.exists(encryptModelScript)) { - logger.info("Downloading WDT to {0}", destLocation); + logger.info("Downloading WDT from {0} to {1}", wdtUrl, destLocation); Files.createDirectories(destLocation.getParent()); OracleHttpClient.downloadFile(wdtUrl, destLocation.toString(), null, null, 3); String cmd = "cd " + destLocation.getParent() + ";unzip " + destLocation; @@ -737,4 +746,16 @@ public static void createEncryptionSecret(String secretName, String namespace) { assertTrue(secretCreated, String.format("create secret failed for %s", secretName)); } } + + private void installOnPremWebLogic() { + Path installScript = Paths.get(RESOURCE_DIR, "bash-scripts", "install-wls.sh"); + String command + = String.format("%s %s %s %s", "/bin/bash", installScript, RESULTS_ROOT, WEBLOGIC_SHIPHOME); + getLogger().info("WebLogic installation command {0}", command); + assertTrue(() -> Command.withParams( + defaultCommandParams() + .command(command) + .redirect(false)) + .execute()); + } } From a928fe80cc59e09c22e18f41fad688091be8ce67 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 2 Apr 2025 17:22:34 -0400 Subject: [PATCH 308/356] Ensure operator or webhook have permission to list and update custom resources --- .../charts/weblogic-operator/templates/_operator-role.tpl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kubernetes/charts/weblogic-operator/templates/_operator-role.tpl b/kubernetes/charts/weblogic-operator/templates/_operator-role.tpl index 0600f6537e9..6d0ef3cc476 100644 --- a/kubernetes/charts/weblogic-operator/templates/_operator-role.tpl +++ b/kubernetes/charts/weblogic-operator/templates/_operator-role.tpl @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. {{- define "operator.operatorRole" }} @@ -17,4 +17,7 @@ rules: - apiGroups: ["admissionregistration.k8s.io"] resources: ["validatingwebhookconfigurations"] verbs: ["get", "create", "update", "patch", "delete"] +- apiGroups: ["weblogic.oracle"] + resources: ["domains", "clusters"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"] {{- end }} From 1022861e505c2cc8ffd91f8b5a3cc71b21e14e6c Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 2 Apr 2025 17:28:06 -0400 Subject: [PATCH 309/356] Update chart unit-test --- .../CreateOperatorGeneratedFilesTestBase.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java index 9e400f0efc0..8f3901a9dc6 100644 --- a/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java +++ b/kubernetes/src/test/java/oracle/kubernetes/operator/create/CreateOperatorGeneratedFilesTestBase.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.create; @@ -722,7 +722,21 @@ private V1Role getExpectedWeblogicOperatorRole() { "delete", "deletecollection"))) .addRulesItem( - newPolicyRuleForValidatingWebhookConfiguration()); + newPolicyRuleForValidatingWebhookConfiguration()) + .addRulesItem( + newPolicyRule() + .addApiGroupsItem("weblogic.oracle") + .resources(asList("domains", "clusters")) + .verbs( + asList( + "get", + "list", + "watch", + "create", + "update", + "patch", + "delete", + "deletecollection"))); } private V1PolicyRule newPolicyRuleForValidatingWebhookConfiguration() { From f04ecbaab5ea7370ca73d65b178345c45fa7edde Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 4 Apr 2025 18:28:10 +0000 Subject: [PATCH 310/356] Merge branch 'fix-spelling' into 'main' misc: fix grammar and spelling in md,sh,txt files See merge request weblogic-cloud/weblogic-kubernetes-operator!4936 (cherry picked from commit 89943037990a0f1549e06c2ec142e572c81dd53b) 21efa6c9 misc: fix grammar and spelling in md,sh,txt files --- README.md | 2 +- documentation/site/content/_index.md | 2 +- documentation/site/content/faq/domain-secret-mismatch.md | 2 +- documentation/site/content/introduction/architecture.md | 2 +- .../site/content/managing-domains/configoverrides/_index.md | 4 ++-- .../site/content/managing-domains/domain-lifecycle/scaling.md | 2 +- .../site/content/managing-domains/domain-on-pv/usage.md | 2 +- .../major-weblogic-version-upgrade/upgrade-14210.md | 2 +- documentation/site/content/managing-operators/overview.md | 2 +- documentation/site/content/managing-operators/preparation.md | 2 +- .../includes/clean-up-resources-body-01.txt | 2 +- .../samples/azure-kubernetes-service/model-in-image.md | 2 +- .../site/content/samples/domains/model-in-image/initial.md | 2 +- .../create-domain-on-aks.sh | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 591919c722d..151ad261ed0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The WebLogic Kubernetes Operator (the “operator”) supports running your WebLogic Server and Fusion Middleware Infrastructure domains on Kubernetes, an industry standard, cloud neutral deployment platform. It lets you encapsulate your entire WebLogic Server installation and layered applications into a portable set of cloud neutral images and simple resource description files. You can run them on any on-premises or public cloud that supports Kubernetes where you've deployed the operator. -Furthermore, the operator is well suited to CI/CD processes. You can easily inject changes when moving between environments, such as from test to production. For example, you can externally inject database URLs and credentials during deployment or you can inject arbitrary changes to most WebLogic configurations. +Furthermore, the operator is well suited to CI/CD processes. You can easily inject changes when moving between environments, such as from test to production. For example, you can externally inject database URLs and credentials during deployment, or you can inject arbitrary changes to most WebLogic configurations. The operator takes advantage of the [Kubernetes operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/), which means that it uses Kubernetes APIs to provide support for operations, such as: provisioning, lifecycle management, application versioning, product patching, scaling, and security. The operator also enables the use of tooling that is native to this infrastructure for monitoring, logging, tracing, and security. diff --git a/documentation/site/content/_index.md b/documentation/site/content/_index.md index b7870e70192..2a96aeb0967 100644 --- a/documentation/site/content/_index.md +++ b/documentation/site/content/_index.md @@ -2,7 +2,7 @@ The WebLogic Kubernetes Operator (the “operator”) supports running your WebLogic Server and Fusion Middleware Infrastructure domains on Kubernetes, an industry standard, cloud neutral deployment platform. It lets you encapsulate your entire WebLogic Server installation and layered applications into a portable set of cloud neutral images and simple resource description files. You can run them on any on-premises or public cloud that supports Kubernetes where you've deployed the operator. -Furthermore, the operator is well suited to CI/CD processes. You can easily inject changes when moving between environments, such as from test to production. For example, you can externally inject database URLs and credentials during deployment or you can inject arbitrary changes to most WebLogic configurations. +Furthermore, the operator is well suited to CI/CD processes. You can easily inject changes when moving between environments, such as from test to production. For example, you can externally inject database URLs and credentials during deployment, or you can inject arbitrary changes to most WebLogic configurations. The operator takes advantage of the [Kubernetes operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/), which means that it uses Kubernetes APIs to provide support for operations, such as: provisioning, lifecycle management, application versioning, product patching, scaling, and security. The operator also enables the use of tooling that is native to this infrastructure for monitoring, logging, tracing, and security. diff --git a/documentation/site/content/faq/domain-secret-mismatch.md b/documentation/site/content/faq/domain-secret-mismatch.md index df1e41cc2ac..3f0971bf610 100644 --- a/documentation/site/content/faq/domain-secret-mismatch.md +++ b/documentation/site/content/faq/domain-secret-mismatch.md @@ -36,7 +36,7 @@ The error occurs while rolling pods have containers based on a new container ima The problem is that WebLogic cannot support server instances being part of the same WebLogic domain if the server instances do not all share the same domain-specific encryption key. Additionally, operator introspection -currently happens only when starting servers following a total shutdown. Therefore, the `boot.properites` files generated from +currently happens only when starting servers following a total shutdown. Therefore, the `boot.properties` files generated from introspecting the image containing the original domain directory will be invalid when used with a container started with the updated container image containing the new or unrelated domain directory. diff --git a/documentation/site/content/introduction/architecture.md b/documentation/site/content/introduction/architecture.md index e09ea7fb66a..88674b813c7 100644 --- a/documentation/site/content/introduction/architecture.md +++ b/documentation/site/content/introduction/architecture.md @@ -113,7 +113,7 @@ The following diagram shows how the various parts of a WebLogic domain are manif This diagram shows the following details: * An optional, persistent volume is created by the customer using one of the available providers. If the persistent volume is shared across the domain or members of a cluster, then the chosen provider must support “Read Write Many” access mode. The shared state on the persistent volume may include the `domain` directory, the `applications` directory, a directory for storing logs, and a directory for any file-based persistence stores. -* A pod is created for the WebLogic Server Administration Server. This pod is named `DOMAIN_UID-wlservername` and is labeled with `weblogic.domainUID`, `weblogic.serverName`, and `weblogic.domainName`. One container runs in this pod. WebLogic Node Manager and Administration Server processes are run inside this container. The Node Manager process is used as an internal implementation detail for the liveness probe which we will descibe in more detail later, for patching, and to provide monitoring and control capabilities to the Administration Console. It is not intended to be used for other purposes, and it may be removed in some future release. +* A pod is created for the WebLogic Server Administration Server. This pod is named `DOMAIN_UID-wlservername` and is labeled with `weblogic.domainUID`, `weblogic.serverName`, and `weblogic.domainName`. One container runs in this pod. WebLogic Node Manager and Administration Server processes are run inside this container. The Node Manager process is used as an internal implementation detail for the liveness probe which we will describe in more detail later, for patching, and to provide monitoring and control capabilities to the Administration Console. It is not intended to be used for other purposes, and it may be removed in some future release. * A `ClusterIP` type service is created for the Administration Server pod. This service provides a stable, well-known network (DNS) name for the Administration Server. This name is derived from the `domainUID` and the Administration Server name as described [here](#network-name-predictability), and it is known before starting up any pod. The Administration Server `ListenAddress` is set to this well-known name. `ClusterIP` type services are only visible inside the Kubernetes cluster. They are used to provide the well-known names that all of the servers in a domain use to communicate with each other. This service is labeled with `weblogic.domainUID` and `weblogic.domainName`. * A `NodePort` type service is optionally created for the Administration Server pod. This service provides HTTP access to the Administration Server to clients that are outside the Kubernetes cluster. This service is intended to be used to access the WebLogic Server Administration Console or for the T3 protocol for WLST connections. This service is labeled with `weblogic.domainUID` and `weblogic.domainName`. * A pod is created for each WebLogic Server Managed Server. These pods are named `DOMAIN_UID-wlservername` and are labeled with `weblogic.domainUID`, `weblogic.serverName`, and `weblogic.domainName`. One container runs in each pod. WebLogic Node Manager and Managed Server processes are run inside each of these containers. The Node Manager process is used as an internal implementation detail for the liveness probe which we will describe in more detail later. It is not intended to be used for other purposes, and it may be removed in some future release. diff --git a/documentation/site/content/managing-domains/configoverrides/_index.md b/documentation/site/content/managing-domains/configoverrides/_index.md index 7eeea192277..e8a1207e2cf 100644 --- a/documentation/site/content/managing-domains/configoverrides/_index.md +++ b/documentation/site/content/managing-domains/configoverrides/_index.md @@ -33,7 +33,7 @@ You can use overrides to customize domains as they are moved from QA to producti * Set your domain `configuration.secrets` to reference the aforementioned Secrets. * If your configuration overrides modify non-dynamic MBean attributes and you currently have WebLogic Server instances from this domain running: * Decide if the changes you are making to non-dynamic MBean attributes can be applied by rolling the affected clusters or Managed Server instances or if the change required a full domain shutdown. - * If a full domain shut down is requried, stop all running WebLogic Server instance Pods in your domain and then restart them. (See [Starting and stopping servers]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#starting-and-stopping-servers" >}}).) + * If a full domain shut down is required, stop all running WebLogic Server instance Pods in your domain and then restart them. (See [Starting and stopping servers]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#starting-and-stopping-servers" >}}).) * Otherwise, simply restart your domain, which includes rolling clusters. (See [Restarting servers]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#restarting-servers" >}}).) * Verify your overrides are taking effect. (See [Debugging](#debugging)). @@ -331,7 +331,7 @@ Best practices for data source modules and their overrides: * Changes to configuration overrides, including the contents of the ConfigMap containing the override templates or the contents of referenced Secrets, do not take effect until the operator runs or repeats its [introspection]({{< relref "/managing-domains/domain-lifecycle/introspection.md" >}}) of the WebLogic domain configuration. * If your configuration overrides modify non-dynamic MBean attributes and you currently have WebLogic Server instances from this domain running: * Decide if the changes you are making to non-dynamic MBean attributes can be applied by rolling the affected clusters or Managed Server instances, or if the change requires a full domain shutdown. (See [Overrides distribution](#overrides-distribution)) - * If a full domain shut down is requried, stop all running WebLogic Server instance Pods in your domain and then restart them. (See [Starting and stopping servers]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#starting-and-stopping-servers" >}}).) + * If a full domain shut down is required, stop all running WebLogic Server instance Pods in your domain and then restart them. (See [Starting and stopping servers]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#starting-and-stopping-servers" >}}).) * Otherwise, simply restart your domain, which includes rolling clusters. (See [Restarting servers]({{< relref "/managing-domains/domain-lifecycle/startup/_index.md#restarting-servers" >}}).) * See [Debugging](#debugging) for ways to check if the configuration overrides are taking effect or if there are errors. diff --git a/documentation/site/content/managing-domains/domain-lifecycle/scaling.md b/documentation/site/content/managing-domains/domain-lifecycle/scaling.md index 5102cd0f426..f65ca71a354 100644 --- a/documentation/site/content/managing-domains/domain-lifecycle/scaling.md +++ b/documentation/site/content/managing-domains/domain-lifecycle/scaling.md @@ -358,7 +358,7 @@ roleRef: ``` #### Horizontal Pod Autoscaler (HPA) using WebLogic Exporter Metrics -Please read this blog post to learn how to scale a WebLogic cluster, based on WebLogic metrics provided by the Monitoring Exporter, using the Kubernetes Horizontal Pod Autoscaler (HPA). We will use the Prometheus Adapter to gather the names of the available metrics from Prometheus at regular intervals. A custom configuration of the adapter will expose only metrics that follow specific formats. [Horizontal Pod Autoscaler (HPA) using WebLogic Exporter Metrics](https://blogs.oracle.com/weblogicserver/post/horizontal-pod-autoscaler-hpausing-weblogic-exporter-metrics). See this corresponding video for a demonstation of the blog post in action. [WebLogic Kubernetes Operator support for Kubernetes Horizontal Pod Autoscaling](https://www.youtube.com/watch?v=aKBG6yJ3sMg). +Please read this blog post to learn how to scale a WebLogic cluster, based on WebLogic metrics provided by the Monitoring Exporter, using the Kubernetes Horizontal Pod Autoscaler (HPA). We will use the Prometheus Adapter to gather the names of the available metrics from Prometheus at regular intervals. A custom configuration of the adapter will expose only metrics that follow specific formats. [Horizontal Pod Autoscaler (HPA) using WebLogic Exporter Metrics](https://blogs.oracle.com/weblogicserver/post/horizontal-pod-autoscaler-hpausing-weblogic-exporter-metrics). See this corresponding video for a demonstration of the blog post in action. [WebLogic Kubernetes Operator support for Kubernetes Horizontal Pod Autoscaling](https://www.youtube.com/watch?v=aKBG6yJ3sMg). #### Using a Prometheus alert action to call the operator's REST scale API In addition to using the WebLogic Diagnostic Framework for automatic scaling of a dynamic cluster, diff --git a/documentation/site/content/managing-domains/domain-on-pv/usage.md b/documentation/site/content/managing-domains/domain-on-pv/usage.md index d7a4bc71166..795d6fb0a93 100644 --- a/documentation/site/content/managing-domains/domain-on-pv/usage.md +++ b/documentation/site/content/managing-domains/domain-on-pv/usage.md @@ -17,7 +17,7 @@ Deploy the operator and ensure that it is monitoring the desired namespace for y ### Configuration -Beginning with operator version 4.1.0, you can provide a section, `domain.spec.configuraiton.initializeDomainOnPV`, to initialize a WebLogic domain on a persistent volume when it is first deployed. +Beginning with operator version 4.1.0, you can provide a section, `domain.spec.configuration.initializeDomainOnPV`, to initialize a WebLogic domain on a persistent volume when it is first deployed. This is a _one time only_ initialization. After the domain is created, subsequent updates to this section in the domain resource YAML file will not recreate or update the WebLogic domain. diff --git a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md index 7ab4537a31a..4b36e401198 100644 --- a/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md +++ b/documentation/site/content/managing-domains/major-weblogic-version-upgrade/upgrade-14210.md @@ -225,7 +225,7 @@ STB.cleartextSchemaPassword = STB.dbaUserName = STB.cleartextDbaPassword = -# This secion is not needed for pure JRF domain. +# This section is not needed for pure JRF domain. # The next section contains the information for performing a schema # upgrade on Oracle WebLogicServer, as described in the Upgrade diff --git a/documentation/site/content/managing-operators/overview.md b/documentation/site/content/managing-operators/overview.md index 692f74d2523..d1d62887295 100644 --- a/documentation/site/content/managing-operators/overview.md +++ b/documentation/site/content/managing-operators/overview.md @@ -25,7 +25,7 @@ A completely installed and running WebLogic Kubernetes Operator environment incl - A pair of Kubernetes custom resource definitions (CRD) for domain and cluster resource that, when installed, enables the Kubernetes API server and the operator to monitor and manage their resource instances. - One or more operator runtimes, each deployed to a different namespace, that monitor Kubernetes namespaces for domain resources. -- A WebLogic domain resource conversion webhook that runs in a single namespacre in the Kubernetes cluster, that is shared by the operator runtimes, and that handles the automatic conversion of older versions of the domain resource. +- A WebLogic domain resource conversion webhook that runs in a single namespace in the Kubernetes cluster, that is shared by the operator runtimes, and that handles the automatic conversion of older versions of the domain resource. - Each operator is associated with a local Kubernetes service account for security purposes. The service account is deployed to the same namespace as the operator. When an operator runtime detects a domain, diff --git a/documentation/site/content/managing-operators/preparation.md b/documentation/site/content/managing-operators/preparation.md index 5cc3f70aa13..5602990b684 100644 --- a/documentation/site/content/managing-operators/preparation.md +++ b/documentation/site/content/managing-operators/preparation.md @@ -321,7 +321,7 @@ helm install weblogic-operator weblogic-operator/weblogic-operator --namespace w ``` {{% notice note %}} -Since this combination of options omits installing the webhook deployment, customers must use the `v9` schema version for Domain resources and manually uprade any `v8` resources from the 3.x version of the operator. +Since this combination of options omits installing the webhook deployment, customers must use the `v9` schema version for Domain resources and manually upgrade any `v8` resources from the 3.x version of the operator. {{% /notice %}} #### Choose a domain namespace selection strategy diff --git a/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt b/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt index 05ad4cf46e6..ca108c210b9 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt +++ b/documentation/site/content/samples/azure-kubernetes-service/includes/clean-up-resources-body-01.txt @@ -1,7 +1,7 @@ If you used the automation script, the output from the `create-domain-on-aks.sh` script includes a statement about the Azure resources created by the script. To delete the cluster and free all related resources, simply delete the resource groups. The output will list the resource groups, such as: ```shell -The following Azure Resouces have been created: +The following Azure Resources have been created: Resource groups: wlsresourcegroup6091605169, MC_wlsresourcegroup6091605169_wlsakscluster6091605169_eastus ``` diff --git a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md index bc49c5224c8..ef96fd6d119 100644 --- a/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md +++ b/documentation/site/content/samples/azure-kubernetes-service/model-in-image.md @@ -353,7 +353,7 @@ $ kubectl -n sample-domain1-ns label secret \ - It must be referenced by the `spec.webLogicCredentialsSecret` field in your Domain resource YAML file. For complete details about the `Domain` resource, see the [Domain resource reference](https://github.com/oracle/weblogic-kubernetes-operator/blob/{{< latestMinorVersion >}}/documentation/domains/Domain.md#domain-spec). - It also must be referenced by macros in the `domainInfo.AdminUserName` and `domainInfo.AdminPassWord` fields in your `model.10.yaml` file. - - The Model WDT runtime encrytion secret: + - The Model WDT runtime encryption secret: - This is a special secret required by Model in Image. - It must contain a `password` field. - It must be referenced using the `spec.model.runtimeEncryptionSecret` field in your Domain resource YAML file. diff --git a/documentation/site/content/samples/domains/model-in-image/initial.md b/documentation/site/content/samples/domains/model-in-image/initial.md index 5872d446402..aae3735b3aa 100644 --- a/documentation/site/content/samples/domains/model-in-image/initial.md +++ b/documentation/site/content/samples/domains/model-in-image/initial.md @@ -28,7 +28,7 @@ The sample uses an `auxiliary image` with the name `wdt-domain-image:WLS-v1` tha ### Deploy resources - Introduction -In this section, you will deploy the domain resource with the new auxliliary image to namespace `sample-domain1-ns`, including the following steps: +In this section, you will deploy the domain resource with the new auxiliary image to namespace `sample-domain1-ns`, including the following steps: - Create a Secret containing your WebLogic administrator user name and password. - Create a Secret containing your Model in Image runtime encryption password: diff --git a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh index d6e8f07c4ef..73aadcbe251 100755 --- a/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh +++ b/kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/create-domain-on-aks.sh @@ -919,7 +919,7 @@ printSummary() { region=${tokens[2]} echo "" echo "" - echo "The following Azure Resouces have been created: " + echo "The following Azure Resources have been created: " echo " Resource groups: ${azureResourceGroupName}, MC_${azureResourceGroupName}_${aksClusterName}_${region}" echo " Kubernetes service cluster name: ${aksClusterName}" echo " Storage account: ${storageAccountName}" From fa2f13928911d6c8ca56ccd47e52924816d93b8f Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 4 Apr 2025 14:40:22 -0400 Subject: [PATCH 311/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6ab9556d987..44867e246d5 100644 --- a/pom.xml +++ b/pom.xml @@ -735,13 +735,13 @@ 1.5.18 4.30.2 2.5.2 - 10.0.2 + 10.1 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json false false - 0.8.12 + 0.8.13 4.9.10 2.70.0 2.7.4 From 3db14260f8020b0819ce0e8df59fae9336263d60 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 4 Apr 2025 18:45:02 +0000 Subject: [PATCH 312/356] Merge branch 'trap' into 'main' Cleanup traps and container exit messages See merge request weblogic-cloud/weblogic-kubernetes-operator!4933 (cherry picked from commit 5166066622a876337202d2078a35eece582072ce) c6a065fa Cleanup traps and container exit messages --- deployment/scripts/operator.sh | 19 +++++++++++++++++- deployment/scripts/webhook.sh | 20 +++++++++++++++++-- .../src/main/resources/scripts/startServer.sh | 17 ++++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/deployment/scripts/operator.sh b/deployment/scripts/operator.sh index e028946c227..0df633fbb3a 100755 --- a/deployment/scripts/operator.sh +++ b/deployment/scripts/operator.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2017, 2023, Oracle and/or its affiliates. +# Copyright (c) 2017, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo "Launching Oracle WebLogic Server Kubernetes Operator..." @@ -9,10 +9,27 @@ relay_SIGTERM() { pid=`grep java /proc/[0-9]*/comm | awk -F / '{ print $3; }'` echo "Sending SIGTERM to java process " $pid kill -SIGTERM $pid + exit 0 } trap relay_SIGTERM SIGTERM +# Relays SIGKILL to all java processes +relay_SIGKILL() { + pid=`grep java /proc/[0-9]*/comm | awk -F / '{ print $3; }'` + echo "Sending SIGKILL to java process " $pid + kill -SIGKILL $pid + exit 0 +} + +trap relay_SIGKILL SIGKILL + +exitMessage() { + echo "Exiting container for the Oracle WebLogic Server Kubernetes Operator..." +} + +trap exitMessage EXIT + DEPLOYMENT_DIR="/deployment" ${DEPLOYMENT_DIR}/initialize-external-operator-identity.sh diff --git a/deployment/scripts/webhook.sh b/deployment/scripts/webhook.sh index 872dd3a1377..29722030225 100755 --- a/deployment/scripts/webhook.sh +++ b/deployment/scripts/webhook.sh @@ -1,8 +1,8 @@ #!/bin/bash -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -echo "Launching the Domain Custom Resource Conversion Webhook for Oracle WebLogic Server Kubernetes Operator..." +echo "Launching the Schema Conversion Webhook for Oracle WebLogic Server Kubernetes Operator..." # Relays SIGTERM to all java processes relay_SIGTERM() { @@ -13,6 +13,22 @@ relay_SIGTERM() { trap relay_SIGTERM SIGTERM +# Relays SIGKILL to all java processes +relay_SIGKILL() { + pid=`grep java /proc/[0-9]*/comm | awk -F / '{ print $3; }'` + echo "Sending SIGKILL to java process " $pid + kill -SIGKILL $pid + exit 0 +} + +trap relay_SIGKILL SIGKILL + +exitMessage() { + echo "Exiting container for the Schema Conversion Webhook for Oracle WebLogic Server Kubernetes Operator..." +} + +trap exitMessage EXIT + if [[ ! -z "$REMOTE_DEBUG_PORT" ]]; then DEBUG="-agentlib:jdwp=transport=dt_socket,server=y,suspend=$DEBUG_SUSPEND,address=*:$REMOTE_DEBUG_PORT" echo "DEBUG=$DEBUG" diff --git a/operator/src/main/resources/scripts/startServer.sh b/operator/src/main/resources/scripts/startServer.sh index ccb81478898..be01127a5d1 100755 --- a/operator/src/main/resources/scripts/startServer.sh +++ b/operator/src/main/resources/scripts/startServer.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2017, 2024, Oracle and/or its affiliates. +# Copyright (c) 2017, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -7,6 +7,19 @@ # This is the script WebLogic Operator WLS Pods use to start their WL Server. # +cleanup() { + trace "Suppressing signal to allow container to exit cleanly." + exit 0 +} + +trap cleanup SIGTERM SIGKILL + +exitMessage() { + trace "Exiting container for WebLogic Server '${SERVER_NAME}'." +} + +trap exitMessage EXIT + if [ -z ${SCRIPTPATH+x} ]; then SCRIPTPATH="$( cd "$(dirname "$0")" > /dev/null 2>&1 ; pwd -P )" fi @@ -312,7 +325,7 @@ else fi # -# Wait forever. Kubernetes will monitor this pod via liveness and readyness probes. +# Wait forever. Kubernetes will monitor this pod via liveness and readiness probes. # waitForShutdownMarker From 5d51a591e5fa14530465d5cd47f6befa2ce316ce Mon Sep 17 00:00:00 2001 From: xian_cao Date: Fri, 4 Apr 2025 20:03:55 +0000 Subject: [PATCH 313/356] fix OCNE Jenkins nightly job for release/4.2 --- .../ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf index ad046dfb785..5137d522786 100644 --- a/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf +++ b/integration-tests/src/test/resources/ocne/terraform/1.9/modules/oci-ocne-images/datasources.tf @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Oracle Corporation and/or affiliates. All rights reserved. +# Copyright (c) 2024, 2025, Oracle Corporation and/or affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl # Gets the OCID of the OS image to use @@ -9,7 +9,7 @@ data "oci_core_images" "OLImageOCID" { #sort_by = "TIMECREATED" #sort_order = "DESC" - display_name = "Oracle-Linux-8.10-2024.08.29-0" + display_name = "Oracle-Autonomous-Linux-8.10-2024.07.31-0" #display_name = "Oracle-Linux-8.10-2024.09.30-0" # filter to avoid Oracle Linux images for GPU #filter { From 50537e7479587f730e8fb5a2cb563f43fb2be394 Mon Sep 17 00:00:00 2001 From: jshum Date: Mon, 7 Apr 2025 08:26:23 -0500 Subject: [PATCH 314/356] fix script error LOG_HOME_LAYOUT --- operator/src/main/resources/scripts/startNodeManager.sh | 4 ++-- operator/src/main/resources/scripts/stopServer.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/operator/src/main/resources/scripts/startNodeManager.sh b/operator/src/main/resources/scripts/startNodeManager.sh index db383fdbf2e..dd1b34a6e43 100644 --- a/operator/src/main/resources/scripts/startNodeManager.sh +++ b/operator/src/main/resources/scripts/startNodeManager.sh @@ -133,7 +133,7 @@ if [ "${SERVER_NAME}" = "introspector" ]; then else # setup ".out" location for a WL server serverLogHome="${LOG_HOME:-${DOMAIN_HOME}}" - if [ -z ${LOG_HOME} ] || [ "ByServers" = ${LOG_HOME_LAYOUT} ] ; then + if [ -z ${LOG_HOME_LAYOUT} ] || [ "ByServers" = ${LOG_HOME_LAYOUT} ] ; then serverLogHome="${serverLogHome}/servers/${SERVER_NAME}/logs" fi export SERVER_OUT_FILE="${serverLogHome}/${SERVER_NAME}.out" @@ -170,7 +170,7 @@ nodemgr_log_file=${NODEMGR_LOG_HOME}/${SERVER_NAME}_nodemanager.log nodemgr_out_file=${NODEMGR_LOG_HOME}/${SERVER_NAME}_nodemanager.out nodemgr_lck_file=${NODEMGR_LOG_HOME}/${SERVER_NAME}_nodemanager.log.lck -if [ -z ${LOG_HOME_LAYOUT} ] || [ "BY_SERVERS" = ${LOG_HOME_LAYOUT} ] ; then +if [ -z ${LOG_HOME_LAYOUT} ] || [ "ByServers" = ${LOG_HOME_LAYOUT} ] ; then nodemgr_log_file=${NODEMGR_LOG_HOME}/servers/${SERVER_NAME}/logs/${SERVER_NAME}_nodemanager.log nodemgr_out_file=${NODEMGR_LOG_HOME}/servers/${SERVER_NAME}/logs//${SERVER_NAME}_nodemanager.out nodemgr_lck_file=${NODEMGR_LOG_HOME}/servers/${SERVER_NAME}/logs//${SERVER_NAME}_nodemanager.log.lck diff --git a/operator/src/main/resources/scripts/stopServer.sh b/operator/src/main/resources/scripts/stopServer.sh index f6cf4c66424..05cea297aed 100755 --- a/operator/src/main/resources/scripts/stopServer.sh +++ b/operator/src/main/resources/scripts/stopServer.sh @@ -15,7 +15,7 @@ source ${SCRIPTPATH}/utils.sh # setup ".out" location for a WL server serverLogHome="${LOG_HOME:-${DOMAIN_HOME}}" -if [ -z ${LOG_HOME_LAYOUT} ] || [ "BY_SERVERS" = ${LOG_HOME_LAYOUT} ] ; then +if [ -z ${LOG_HOME_LAYOUT} ] || [ "ByServers" = ${LOG_HOME_LAYOUT} ] ; then serverLogHome="${serverLogHome}/servers/${SERVER_NAME}/logs" fi STOP_OUT_FILE="${serverLogHome}/${SERVER_NAME}.stop.out" From 6ed78814c95d2b22eea4b8e48924a9ca672a2b32 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Sun, 13 Apr 2025 20:27:21 +0000 Subject: [PATCH 315/356] updated wls and db images to use 14.1.2.0.0 and 19.3.0.0 ( for db) --- Jenkinsfile.oke | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile.oke b/Jenkinsfile.oke index 747ba8f1154..3392565b205 100644 --- a/Jenkinsfile.oke +++ b/Jenkinsfile.oke @@ -168,16 +168,16 @@ pipeline { defaultValue: 'test-images/weblogic' ) string(name: 'WEBLOGIC_IMAGE_TAG', - description: '12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', - defaultValue: '12.2.1.4' + description: '14.1.2.0-generic-jdk17-ol8, 14.1.2.0-generic-jdk17-ol9, 14.1.2.0-generic-jdk21-ol8, 14.1.2.0-generic-jdk21-ol9, 12.2.1.4, 12.2.1.4-dev(12.2.1.4-dev-ol7) , 12.2.1.4-slim(12.2.1.4-slim-ol7), 12.2.1.4-ol8, 12.2.1.4-dev-ol8, 12.2.1.4-slim-ol8, 14.1.1.0-11-ol7, 14.1.1.0-dev-11-ol7, 14.1.1.0-slim-11-ol7, 14.1.1.0-8-ol7, 14.1.1.0-dev-8-ol7, 14.1.1.0-slim-8-ol7, 14.1.1.0-11-ol8, 14.1.1.0-dev-11-ol8, 14.1.1.0-slim-11-ol8, 14.1.1.0-8-ol8, 14.1.1.0-dev-8-ol8, 14.1.1.0-slim-8-ol8', + defaultValue: '14.1.2.0-generic-jdk17-ol8' ) string(name: 'FMWINFRA_IMAGE_NAME', description: 'FWM Infra image name. Default is the image name in OCIR. Use middleware/fmw-infrastructure for OCR.', defaultValue: 'test-images/fmw-infrastructure' ) string(name: 'FMWINFRA_IMAGE_TAG', - description: 'FWM Infra image tag', - defaultValue: '12.2.1.4' + description: '14.1.2.0-jdk17-ol8, 14.1.2.0-jdk17-ol9, 14.1.2.0-jdk21-ol8, 14.1.2.0-jdk21-ol9', + defaultValue: '14.1.2.0-jdk17-ol8' ) string(name: 'DB_IMAGE_NAME', description: 'Oracle DB image name. Default is the image name in OCIR, use database/enterprise for OCR.', @@ -185,7 +185,7 @@ pipeline { ) string(name: 'DB_IMAGE_TAG', description: 'Oracle DB image tag', - defaultValue: '12.2.0.1-slim' + defaultValue: '19.3.0.0' ) string(name: 'MONITORING_EXPORTER_BRANCH', description: '', From 32bf09c95896d48c39c986bef64b9e4de7af6ecd Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sun, 13 Apr 2025 20:29:09 +0000 Subject: [PATCH 316/356] Merge branch 'trap-more' into 'main' Trap signals in additional files See merge request weblogic-cloud/weblogic-kubernetes-operator!4937 (cherry picked from commit 61a33ecd2fa6a29436654d157be336e3ccc05996) 6a14f8db Trap signals in additional files --- .../resources/scripts/initializeDomainHomeOnPV.sh | 15 ++++++++++++++- .../main/resources/scripts/introspectDomain.sh | 14 +++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/operator/src/main/resources/scripts/initializeDomainHomeOnPV.sh b/operator/src/main/resources/scripts/initializeDomainHomeOnPV.sh index f459b4813b1..14b333066da 100644 --- a/operator/src/main/resources/scripts/initializeDomainHomeOnPV.sh +++ b/operator/src/main/resources/scripts/initializeDomainHomeOnPV.sh @@ -1,5 +1,18 @@ #!/bin/sh -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. + +cleanup() { + trace "Suppressing signal to allow container to exit cleanly." + exit 0 +} + +trap cleanup SIGTERM SIGKILL + +exitMessage() { + trace "Exiting container for initializing domain home on PV of '${DOMAIN_UID}'." +} + +trap exitMessage EXIT scriptDir="$( cd "$(dirname "$0")" > /dev/null 2>&1 ; pwd -P )" if [ "${debug}" == "true" ]; then set -x; fi; diff --git a/operator/src/main/resources/scripts/introspectDomain.sh b/operator/src/main/resources/scripts/introspectDomain.sh index 1da3c2db52e..d83738ce4af 100755 --- a/operator/src/main/resources/scripts/introspectDomain.sh +++ b/operator/src/main/resources/scripts/introspectDomain.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2018, 2024, Oracle and/or its affiliates. +# Copyright (c) 2018, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -44,6 +44,18 @@ # and introspectDomain.py (see these scripts to find out what else needs to be set). # +cleanup() { + trace "Suppressing signal to allow container to exit cleanly." + exit 0 +} + +trap cleanup SIGTERM SIGKILL + +exitMessage() { + trace "Exiting container for introspection of '${DOMAIN_UID}'." +} + +trap exitMessage EXIT SCRIPTPATH="$( cd "$(dirname "$0")" > /dev/null 2>&1 ; pwd -P )" From 09ab3b0ec2cf7ecf3ad812b4112b2429d535516a Mon Sep 17 00:00:00 2001 From: johnny_shum Date: Mon, 14 Apr 2025 13:52:49 +0000 Subject: [PATCH 317/356] Back port from main allowreadonly --- .../kubernetes/common/CommonConstants.java | 3 +- .../operator/helpers/BasePodStepContext.java | 41 ++++++++++--- .../operator/helpers/FluentbitHelper.java | 11 +++- .../operator/helpers/FluentdHelper.java | 9 ++- .../operator/helpers/JobStepContext.java | 58 +++++++++++++------ .../operator/helpers/PodStepContext.java | 31 ++++++++-- .../helpers/StepContextConstants.java | 1 + .../main/resources/scripts/modelInImage.sh | 4 +- .../operator/helpers/JobHelperTest.java | 20 +++++++ .../operator/helpers/PodHelperTestBase.java | 28 ++++++++- 10 files changed, 168 insertions(+), 38 deletions(-) diff --git a/common/src/main/java/oracle/kubernetes/common/CommonConstants.java b/common/src/main/java/oracle/kubernetes/common/CommonConstants.java index a2f2b11449c..45e4df920a7 100644 --- a/common/src/main/java/oracle/kubernetes/common/CommonConstants.java +++ b/common/src/main/java/oracle/kubernetes/common/CommonConstants.java @@ -18,7 +18,8 @@ private CommonConstants() { public static final String SCRIPTS_VOLUME = "weblogic-scripts-cm-volume"; public static final String SCRIPTS_MOUNTS_PATH = "/weblogic-operator/scripts"; - + public static final String TMPDIR_VOLUME = "weblogic-tmpdir-volume"; + public static final String TMPDIR_MOUNTS_PATH = "/tmp"; public static final String SECRETS_WEBHOOK_CERT = "/secrets/webhookCert"; public static final String SECRETS_WEBHOOK_KEY = "/secrets/webhookKey"; diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java index fe065b4da1e..9151571bcef 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java @@ -4,7 +4,6 @@ package oracle.kubernetes.operator.helpers; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -44,6 +43,8 @@ import static oracle.kubernetes.common.CommonConstants.COMPATIBILITY_MODE; import static oracle.kubernetes.common.CommonConstants.SCRIPTS_MOUNTS_PATH; import static oracle.kubernetes.common.CommonConstants.SCRIPTS_VOLUME; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.common.CommonConstants.WLS_SHARED; import static oracle.kubernetes.common.helpers.AuxiliaryImageEnvVars.AUXILIARY_IMAGE_PATHS; import static oracle.kubernetes.weblogic.domain.model.AuxiliaryImage.AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME; @@ -74,6 +75,13 @@ String getSizeLimit() { return info.getDomain().getAuxiliaryImageVolumeSizeLimit(); } + boolean isReadOnlyRootFileSystem() { + return Optional.ofNullable(getServerSpec()) + .map(EffectiveServerSpec::getContainerSecurityContext) + .map(V1SecurityContext::getReadOnlyRootFilesystem) + .orElse(false); + } + String getMedium() { return info.getDomain().getAuxiliaryImageVolumeMedium(); } @@ -106,7 +114,7 @@ private boolean hasMatchingVolumeName(V1VolumeMount volumeMount) { abstract List getFluentbitVolumes(); protected V1Container createPrimaryContainer() { - return new V1Container() + V1Container container = new V1Container() .name(getContainerName()) .image(getServerSpec().getImage()) .imagePullPolicy(getServerSpec().getImagePullPolicy()) @@ -115,6 +123,17 @@ protected V1Container createPrimaryContainer() { .envFrom(getEnvFrom()) .resources(getResources()) .securityContext(getServerSpec().getContainerSecurityContext()); + + if (isReadOnlyRootFileSystem()) { + List mounts = container.getVolumeMounts(); + if (mounts == null) { + mounts = new ArrayList<>(); + } + mounts.add(new V1VolumeMount().name(TMPDIR_VOLUME).mountPath(TMPDIR_MOUNTS_PATH)); + container.volumeMounts(mounts); + } + + return container; } protected abstract List getEnvFrom(); @@ -160,11 +179,19 @@ protected V1Container createInitContainerForAuxiliaryImage(DeploymentImage auxil .imagePullPolicy(auxiliaryImage.getImagePullPolicy()) .command(Collections.singletonList(AUXILIARY_IMAGE_INIT_CONTAINER_WRAPPER_SCRIPT)) .env(createEnv(auxiliaryImage, getName(index))) - .resources(createResources()) - .volumeMounts(Arrays.asList( - new V1VolumeMount().name(AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME) - .mountPath(AUXILIARY_IMAGE_TARGET_PATH), - new V1VolumeMount().name(SCRIPTS_VOLUME).mountPath(SCRIPTS_MOUNTS_PATH))); + .resources(createResources()); + + List mounts = new ArrayList<>(); + + if (isReadOnlyRootFileSystem()) { + mounts.add(new V1VolumeMount().name(TMPDIR_VOLUME).mountPath(TMPDIR_MOUNTS_PATH)); + } + + mounts.add(new V1VolumeMount().name(AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME) + .mountPath(AUXILIARY_IMAGE_TARGET_PATH)); + mounts.add(new V1VolumeMount().name(SCRIPTS_VOLUME).mountPath(SCRIPTS_MOUNTS_PATH)); + + container.volumeMounts(mounts); if (isInitializeDomainOnPV) { container.securityContext(PodSecurityHelper.getDefaultContainerSecurityContext()); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java index c0e1d698e07..592067f87ef 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2024, Oracle and/or its affiliates. +// Copyright (c) 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -21,6 +21,8 @@ import oracle.kubernetes.weblogic.domain.model.DomainResource; import oracle.kubernetes.weblogic.domain.model.FluentbitSpecification; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTBIT_CONFIGMAP_NAME_SUFFIX; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTBIT_CONFIGMAP_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTBIT_CONFIG_DATA_NAME; @@ -39,7 +41,7 @@ private FluentbitHelper() { * @param domain Domain. */ public static void addFluentbitContainer(FluentbitSpecification fluentbitSpecification, List containers, - DomainResource domain, boolean isJobPod) { + DomainResource domain, boolean isJobPod, boolean isReadOnlyRootFileSystem) { V1Container fluentbitContainer = new V1Container(); fluentbitContainer.name(FLUENTBIT_CONTAINER_NAME); @@ -66,6 +68,11 @@ public static void addFluentbitContainer(FluentbitSpecification fluentbitSpecifi fluentbitSpecification.getVolumeMounts().forEach(fluentbitContainer::addVolumeMountsItem); fluentbitContainer.addVolumeMountsItem(createFluentbitConfigmapVolumeMount()); + + if (isReadOnlyRootFileSystem) { + fluentbitContainer.addVolumeMountsItem(new V1VolumeMount().name(TMPDIR_VOLUME).mountPath(TMPDIR_MOUNTS_PATH)); + } + containers.add(fluentbitContainer); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java index 4085877d35b..e5d9c3dfdc2 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java @@ -21,6 +21,8 @@ import oracle.kubernetes.weblogic.domain.model.DomainResource; import oracle.kubernetes.weblogic.domain.model.FluentdSpecification; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTD_CONFIGMAP_NAME_SUFFIX; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTD_CONFIGMAP_VOLUME; import static oracle.kubernetes.operator.helpers.StepContextConstants.FLUENTD_CONFIG_DATA_NAME; @@ -39,7 +41,7 @@ private FluentdHelper() { * @param domain Domain. */ public static void addFluentdContainer(FluentdSpecification fluentdSpecification, List containers, - DomainResource domain, boolean isJobPod) { + DomainResource domain, boolean isJobPod, boolean isReadOnlyRootFileSystem) { V1Container fluentdContainer = new V1Container(); @@ -69,6 +71,11 @@ public static void addFluentdContainer(FluentdSpecification fluentdSpecification .forEach(fluentdContainer::addVolumeMountsItem); fluentdContainer.addVolumeMountsItem(createFluentdConfigmapVolumeMount()); + + if (isReadOnlyRootFileSystem) { + fluentdContainer.addVolumeMountsItem(new V1VolumeMount().name(TMPDIR_VOLUME).mountPath(TMPDIR_MOUNTS_PATH)); + } + containers.add(fluentdContainer); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 813cf2e16e1..4397655b505 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -16,6 +16,7 @@ import io.kubernetes.client.openapi.models.V1ConfigMapVolumeSource; import io.kubernetes.client.openapi.models.V1Container; +import io.kubernetes.client.openapi.models.V1EmptyDirVolumeSource; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1Job; @@ -69,6 +70,8 @@ import static oracle.kubernetes.common.CommonConstants.COMPATIBILITY_MODE; import static oracle.kubernetes.common.CommonConstants.SCRIPTS_MOUNTS_PATH; import static oracle.kubernetes.common.CommonConstants.SCRIPTS_VOLUME; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.common.CommonConstants.WLS_SHARED; import static oracle.kubernetes.common.utils.CommonUtils.MAX_ALLOWED_VOLUME_NAME_LENGTH; import static oracle.kubernetes.common.utils.CommonUtils.VOLUME_NAME_SUFFIX; @@ -300,6 +303,9 @@ String getJobCreatedMessageKey() { } String getNodeManagerHome() { + if (isReadOnlyRootFileSystem()) { + return NODEMGR_HOME_READONLY_ROOT; + } return NODEMGR_HOME; } @@ -492,23 +498,29 @@ private Optional getInitializeDomainOnPV() { } private void addInitDomainOnPVInitContainer(List initContainers) { - initContainers.add(new V1Container() - .name(INIT_DOMAIN_ON_PV_CONTAINER) - .image(getDomain().getSpec().getImage()) - .volumeMounts(getDomain().getAdminServerSpec().getAdditionalVolumeMounts()) - .addVolumeMountsItem(new V1VolumeMount().name(SCRIPTS_VOLUME).mountPath(SCRIPTS_MOUNTS_PATH)) - .addVolumeMountsItem(new V1VolumeMount().name(AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME) - .mountPath(AUXILIARY_IMAGE_TARGET_PATH)) - .env(getDomain().getAdminServerSpec().getEnvironmentVariables()) - .addEnvItem(new V1EnvVar().name(DOMAIN_HOME).value(getDomainHome())) - .addEnvItem(new V1EnvVar().name(ServerEnvVars.LOG_HOME).value(getEffectiveLogHome())) - .addEnvItem(new V1EnvVar().name(ServerEnvVars.DOMAIN_HOME_ON_PV_DEFAULT_UGID) - .value(getDomainHomeOnPVHomeOwnership())) - .addEnvItem(new V1EnvVar().name(AuxiliaryImageEnvVars.AUXILIARY_IMAGE_TARGET_PATH) - .value(AuxiliaryImageConstants.AUXILIARY_IMAGE_TARGET_PATH)) - .securityContext(getInitContainerSecurityContext()) - .command(List.of(INIT_DOMAIN_ON_PV_SCRIPT)) - ); + V1Container container = new V1Container() + .name(INIT_DOMAIN_ON_PV_CONTAINER) + .image(getDomain().getSpec().getImage()) + .volumeMounts(getDomain().getAdminServerSpec().getAdditionalVolumeMounts()) + .addVolumeMountsItem(new V1VolumeMount().name(SCRIPTS_VOLUME).mountPath(SCRIPTS_MOUNTS_PATH)) + .addVolumeMountsItem(new V1VolumeMount().name(AUXILIARY_IMAGE_INTERNAL_VOLUME_NAME) + .mountPath(AUXILIARY_IMAGE_TARGET_PATH)) + .env(getDomain().getAdminServerSpec().getEnvironmentVariables()) + .addEnvItem(new V1EnvVar().name(DOMAIN_HOME).value(getDomainHome())) + .addEnvItem(new V1EnvVar().name(ServerEnvVars.LOG_HOME).value(getEffectiveLogHome())) + .addEnvItem(new V1EnvVar().name(ServerEnvVars.DOMAIN_HOME_ON_PV_DEFAULT_UGID) + .value(getDomainHomeOnPVHomeOwnership())) + .addEnvItem(new V1EnvVar().name(AuxiliaryImageEnvVars.AUXILIARY_IMAGE_TARGET_PATH) + .value(AuxiliaryImageConstants.AUXILIARY_IMAGE_TARGET_PATH)) + .securityContext(getInitContainerSecurityContext()) + .command(List.of(INIT_DOMAIN_ON_PV_SCRIPT)); + + if (isReadOnlyRootFileSystem()) { + container.addVolumeMountsItem(volumeMount(TMPDIR_VOLUME, TMPDIR_MOUNTS_PATH)); + } + + initContainers.add(container); + } @Override @@ -633,6 +645,14 @@ protected V1PodSpec createPodSpec() { } } + if (isReadOnlyRootFileSystem()) { + List volumes = podSpec.getVolumes(); + if (volumes == null) { + volumes = new ArrayList<>(); + } + volumes.add(new V1Volume().name(TMPDIR_VOLUME).emptyDir(new V1EmptyDirVolumeSource().medium("Memory"))); + } + getConfigOverrideSecrets().forEach(secretName -> addConfigOverrideSecretVolume(podSpec, secretName)); Optional.ofNullable(getConfigOverrides()).ifPresent(overrides -> addConfigOverrideVolume(podSpec, overrides)); @@ -822,7 +842,7 @@ protected List getContainers() { .ifPresent(fluentd -> { if (Boolean.TRUE.equals(fluentd.getWatchIntrospectorLogs())) { FluentdHelper.addFluentdContainer(fluentd, - containers, getDomain(), true); + containers, getDomain(), true, isReadOnlyRootFileSystem()); } }); @@ -830,7 +850,7 @@ protected List getContainers() { .ifPresent(fluentbit -> { if (Boolean.TRUE.equals(fluentbit.getWatchIntrospectorLogs())) { FluentbitHelper.addFluentbitContainer(fluentbit, - containers, getDomain(), true); + containers, getDomain(), true, isReadOnlyRootFileSystem()); } }); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index a5fec121c83..2bac7de476b 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2024, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -28,6 +28,7 @@ import io.kubernetes.client.openapi.models.V1ContainerBuilder; import io.kubernetes.client.openapi.models.V1ContainerPort; import io.kubernetes.client.openapi.models.V1DeleteOptions; +import io.kubernetes.client.openapi.models.V1EmptyDirVolumeSource; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1ExecAction; @@ -89,6 +90,8 @@ import static oracle.kubernetes.common.AuxiliaryImageConstants.AUXILIARY_IMAGE_VOLUME_NAME_OLD_PREFIX; import static oracle.kubernetes.common.AuxiliaryImageConstants.AUXILIARY_IMAGE_VOLUME_NAME_PREFIX; import static oracle.kubernetes.common.CommonConstants.COMPATIBILITY_MODE; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.common.helpers.AuxiliaryImageEnvVars.AUXILIARY_IMAGE_MOUNT_PATH; import static oracle.kubernetes.common.logging.MessageKeys.CYCLING_POD_EVICTED; import static oracle.kubernetes.common.logging.MessageKeys.CYCLING_POD_SPEC_CHANGED; @@ -737,6 +740,9 @@ private List getVolumes(String domainUid) { } volumes.addAll(getServerSpec().getAdditionalVolumes()); + if (isReadOnlyRootFileSystem()) { + volumes.add(new V1Volume().name(TMPDIR_VOLUME).emptyDir(new V1EmptyDirVolumeSource().medium("Memory"))); + } return volumes; } @@ -776,10 +782,13 @@ protected List getContainerCommand() { protected List getContainers() { List containers = new ArrayList<>(getServerSpec().getContainers()); exporterContext.addContainer(containers); + boolean isReadOnlyRootFileSystem = isReadOnlyRootFileSystem(); Optional.ofNullable(getDomain().getFluentdSpecification()) - .ifPresent(fluentd -> addFluentdContainer(fluentd, containers, getDomain(), false)); + .ifPresent(fluentd -> addFluentdContainer(fluentd, containers, getDomain(), false, + isReadOnlyRootFileSystem)); Optional.ofNullable(getDomain().getFluentbitSpecification()) - .ifPresent(fluentbit -> addFluentbitContainer(fluentbit, containers, getDomain(), false)); + .ifPresent(fluentbit -> addFluentbitContainer(fluentbit, containers, getDomain(), + false, isReadOnlyRootFileSystem)); return containers; } @@ -838,7 +847,7 @@ void addStartupEnvVars(List vars) { addEnvVarIfTrue(isAdminServerProtocolChannelSecure(), vars, ServerEnvVars.ADMIN_SERVER_PORT_SECURE); addEnvVar(vars, ServerEnvVars.SERVER_NAME, getServerName()); addEnvVar(vars, ServerEnvVars.DOMAIN_UID, getDomainUid()); - addEnvVar(vars, ServerEnvVars.NODEMGR_HOME, NODEMGR_HOME); + addEnvVar(vars, ServerEnvVars.NODEMGR_HOME, getNodeManagerHome()); addEnvVar(vars, ServerEnvVars.LOG_HOME, getEffectiveLogHome()); if (getLogHomeLayout() == LogHomeLayoutType.FLAT) { addEnvVar(vars, ServerEnvVars.LOG_HOME_LAYOUT, getLogHomeLayout().toString()); @@ -869,6 +878,12 @@ protected void addAuxiliaryImageEnv(List auxiliaryImageList, Lis }); } + String getNodeManagerHome() { + if (isReadOnlyRootFileSystem()) { + return NODEMGR_HOME_READONLY_ROOT; + } + return NODEMGR_HOME; + } private String getDomainHome() { return getDomain().getDomainHome(); @@ -1648,7 +1663,7 @@ void addContainer(List containers) { } private V1Container createMonitoringExporterContainer() { - return new V1Container() + V1Container container = new V1Container() .name(EXPORTER_CONTAINER_NAME) .image(getDomain().getMonitoringExporterImage()) .imagePullPolicy(getDomain().getMonitoringExporterImagePullPolicy()) @@ -1657,6 +1672,12 @@ private V1Container createMonitoringExporterContainer() { .addEnvItem(new V1EnvVar().name("JAVA_OPTS").value(createJavaOptions())) .addPortsItem(new V1ContainerPort() .name("metrics").protocol("TCP").containerPort(getPort())); + + if (isReadOnlyRootFileSystem()) { + container.addVolumeMountsItem(new V1VolumeMount().name(TMPDIR_VOLUME).mountPath(TMPDIR_MOUNTS_PATH)); + } + + return container; } private String createJavaOptions() { diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java index f36619c5f2d..6a40ffd9fca 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/StepContextConstants.java @@ -32,6 +32,7 @@ public interface StepContextConstants { String OPSS_WALLETFILE_MOUNT_PATH = "/weblogic-operator/opss-walletfile-secret"; String DEBUG_CM_MOUNTS_PATH = "/weblogic-operator/debug"; String NODEMGR_HOME = "/u01/nodemanager"; + String NODEMGR_HOME_READONLY_ROOT = "/tmp/nodemanager"; String INIT_DOMAIN_ON_PV_CONTAINER = "create-dh-dir"; String INIT_DOMAIN_ON_PV_SCRIPT = "/weblogic-operator/scripts/initializeDomainHomeOnPV.sh"; diff --git a/operator/src/main/resources/scripts/modelInImage.sh b/operator/src/main/resources/scripts/modelInImage.sh index 16e6ee0fde9..114b80d6092 100755 --- a/operator/src/main/resources/scripts/modelInImage.sh +++ b/operator/src/main/resources/scripts/modelInImage.sh @@ -172,7 +172,7 @@ buildWDTParams_MD5() { model_list="" archive_list="" - variable_list="/u01/_k8s_generated_props.properties" + variable_list="/tmp/_k8s_generated_props.properties" # # First build the command line parameters for WDT @@ -396,7 +396,7 @@ createWLDomain() { # create domain again DISABLE_SM_FOR_12214_NONSM_UPG=0 - if [ -f ${PRIMORDIAL_DOMAIN_ZIPPED} ] && [ -z "${MII_USE_ONLINE_UPDATE}" ] || [ "${MII_USE_ONLINE_UPDATE}" != "true" ] ]; then + if [ -f ${PRIMORDIAL_DOMAIN_ZIPPED} ] && { [ -z "${MII_USE_ONLINE_UPDATE}" ] || [ "${MII_USE_ONLINE_UPDATE}" != "true" ]; }; then checkSecureModeForUpgrade fi if [ ${WDT_ARTIFACTS_CHANGED} -ne 0 ] || [ ${jdk_changed} -eq 1 ] \ diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java index 136022c42e6..358f2ec7e15 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/JobHelperTest.java @@ -29,6 +29,7 @@ import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1ConfigMapEnvSource; import io.kubernetes.client.openapi.models.V1Container; +import io.kubernetes.client.openapi.models.V1EmptyDirVolumeSource; import io.kubernetes.client.openapi.models.V1EnvFromSource; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1Job; @@ -83,6 +84,8 @@ import static com.meterware.simplestub.Stub.createNiceStub; import static oracle.kubernetes.common.CommonConstants.COMPATIBILITY_MODE; import static oracle.kubernetes.common.CommonConstants.SCRIPTS_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.common.CommonConstants.WLS_SHARED; import static oracle.kubernetes.common.logging.MessageKeys.FLUENTD_CONFIGMAP_CREATED; import static oracle.kubernetes.common.logging.MessageKeys.FLUENTD_CONFIGMAP_REPLACED; @@ -92,6 +95,7 @@ import static oracle.kubernetes.operator.DomainProcessorTestSetup.UID; import static oracle.kubernetes.operator.DomainProcessorTestSetup.createTestDomain; import static oracle.kubernetes.operator.LabelConstants.INTROSPECTION_DOMAIN_SPEC_GENERATION; +import static oracle.kubernetes.operator.ProcessingConstants.CLUSTER_NAME; import static oracle.kubernetes.operator.ProcessingConstants.DEFAULT_JRF_INTROSPECTOR_JOB_ACTIVE_DEADLINE_SECONDS; import static oracle.kubernetes.operator.ProcessingConstants.DEFAULT_WLS_OR_RESTRICTED_JRF_INTROSPECTOR_JOB_ACTIVE_DEADLINE_SECONDS; import static oracle.kubernetes.operator.ProcessingConstants.DOMAIN_TOPOLOGY; @@ -1255,6 +1259,22 @@ void verify_introspectorPodWithInitializeWLSDomainOnPVSpec_activeDeadlineSeconds jobSpec.getActiveDeadlineSeconds(), is(DEFAULT_WLS_OR_RESTRICTED_JRF_INTROSPECTOR_JOB_ACTIVE_DEADLINE_SECONDS)); } + @Test + void whenDomainHasReadOnlyRootFileSystem_verifyVolumesAndMounts() throws NoSuchFieldException { + + configureDomain() + .withContainerSecurityContext(new V1SecurityContext().readOnlyRootFilesystem(true)) + .configureCluster(domainPresenceInfo, CLUSTER_NAME); + + runCreateJob(); + assertThat(getJobVolumeMounts(), + hasItem(new V1VolumeMount().name(TMPDIR_VOLUME) + .mountPath(TMPDIR_MOUNTS_PATH))); + assertThat(getJobVolumes(), + hasItem(new V1Volume().name(TMPDIR_VOLUME).emptyDir(new V1EmptyDirVolumeSource().medium("Memory")))); + + } + @Test void verify_introspectorPodSpec_activeDeadlineSeconds_domain_overrides_values() { configureDomain().withIntrospectorJobActiveDeadlineSeconds(600L); diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java index 8f5b2b9ed26..5e7f28d326d 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -109,6 +109,8 @@ import static oracle.kubernetes.common.AuxiliaryImageConstants.AUXILIARY_IMAGE_VOLUME_NAME_PREFIX; import static oracle.kubernetes.common.CommonConstants.COMPATIBILITY_MODE; import static oracle.kubernetes.common.CommonConstants.SCRIPTS_VOLUME; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_MOUNTS_PATH; +import static oracle.kubernetes.common.CommonConstants.TMPDIR_VOLUME; import static oracle.kubernetes.common.helpers.AuxiliaryImageEnvVars.AUXILIARY_IMAGE_COMMAND; import static oracle.kubernetes.common.helpers.AuxiliaryImageEnvVars.AUXILIARY_IMAGE_CONTAINER_IMAGE; import static oracle.kubernetes.common.helpers.AuxiliaryImageEnvVars.AUXILIARY_IMAGE_CONTAINER_NAME; @@ -137,6 +139,7 @@ import static oracle.kubernetes.operator.KubernetesConstants.WLS_CONTAINER_NAME; import static oracle.kubernetes.operator.LabelConstants.MII_UPDATED_RESTART_REQUIRED_LABEL; import static oracle.kubernetes.operator.LabelConstants.OPERATOR_VERSION; +import static oracle.kubernetes.operator.ProcessingConstants.CLUSTER_NAME; import static oracle.kubernetes.operator.ProcessingConstants.ENVVARS; import static oracle.kubernetes.operator.ProcessingConstants.MAKE_RIGHT_DOMAIN_OPERATION; import static oracle.kubernetes.operator.ProcessingConstants.MII_DYNAMIC_UPDATE; @@ -1148,6 +1151,29 @@ protected String getLegacyAuxiliaryImageVolumeName(String testVolumeName) { return COMPATIBILITY_MODE + AUXILIARY_IMAGE_VOLUME_NAME_PREFIX + testVolumeName; } + @Test + void whenDomainSetReadOnlyRootFileSystem_verifyVolumesAndMounts() { + getConfigurator() + .withContainerSecurityContext(new V1SecurityContext().readOnlyRootFilesystem(true)) + .withAuxiliaryImages(getAuxiliaryImages("wdt-image:v1")) + .configureCluster(domainPresenceInfo, CLUSTER_NAME); + + testSupport.addToPacket(CLUSTER_NAME, CLUSTER_NAME); + + List containers = getCreatedPodSpecContainers(); + for (V1Container container : containers) { + assertThat(container.getVolumeMounts(), + hasItem(new V1VolumeMount().name(TMPDIR_VOLUME) + .mountPath(TMPDIR_MOUNTS_PATH))); + + } + + assertThat(getCreatedPod().getSpec().getVolumes(), + hasItem(new V1Volume().name(TMPDIR_VOLUME).emptyDir(new V1EmptyDirVolumeSource().medium("Memory")))); + + } + + @Test void whenDomainHasLegacyAuxiliaryImageAndVolumeWithCustomMountPath_createPodsWithVolumeMountHavingCustomMountPath() { Map auxiliaryImageVolume = createAuxiliaryImageVolume(TEST_VOLUME_NAME, CUSTOM_MOUNT_PATH); From e39bb15d5d9678f0f757139d9abe49d1bda565d3 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 14 Apr 2025 10:43:44 -0400 Subject: [PATCH 318/356] Dependency updates --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 44867e246d5..06204034617 100644 --- a/pom.xml +++ b/pom.xml @@ -708,13 +708,13 @@ 3.6.0 1.0.0 3.27.3 - 2.18.0 + 2.19.0 4.3.0 19.0.2 3.0.1u2 2.1.20 4.12.0 - 3.10.2 + 3.11.0 1.80 5.12.1 5.7.1 @@ -730,12 +730,12 @@ 2.18.3 2.4 2.12.1 - 12.1.0 + 12.1.1 2.0.17 1.5.18 4.30.2 2.5.2 - 10.1 + 10.2 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 2316d0f2268211093d6151e0cc0f6bc8a3a8adf0 Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 14 Apr 2025 16:37:45 +0000 Subject: [PATCH 319/356] Fixed firealert issue in MonitoringExporterSamples test - OWLS-127302 --- .../ItMonitoringExporterSamples.java | 16 ++++++++--- .../kubernetes/utils/MonitoringUtils.java | 27 +++++++++++++++++++ .../test/resources/exporter/promvalues.yaml | 4 +-- .../resources/exporter/promvaluesoke.yaml | 4 +-- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java index 41d0ee554b6..9a6b4de8bd5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMonitoringExporterSamples.java @@ -107,6 +107,7 @@ import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.createIngressForDomainAndVerify; import static oracle.weblogic.kubernetes.utils.LoadBalancerUtils.installAndVerifyNginx; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.checkMetricsViaPrometheus; +import static oracle.weblogic.kubernetes.utils.MonitoringUtils.checkPrometheusAlert; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.cleanupPromGrafanaClusterRoles; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.createAndVerifyDomain; import static oracle.weblogic.kubernetes.utils.MonitoringUtils.createAndVerifyMiiImage; @@ -379,7 +380,7 @@ void testEndToEndViaChart() throws Exception { } } - private void fireAlert() throws ApiException { + private void fireAlert() throws Exception { // scale domain2 logger.info("Scaling cluster {0} of domain {1} in namespace {2} to {3} servers.", cluster1Name, domain2Uid, domain2Namespace, 1); @@ -388,6 +389,15 @@ private void fireAlert() throws ApiException { domain2Uid + "-" + MANAGED_SERVER_NAME_BASE, replicaCount, managedServersCount, null, null); + logger.info("Wait for the prometheus to create alert "); + checkPrometheusAlert("ClusterWarning", hostPortPrometheus, + prometheusReleaseName + + "." + monitoringNS); + + logger.info("Wait for the prometheus to fire alert "); + checkPrometheusAlert("firing", hostPortPrometheus, + prometheusReleaseName + + "." + monitoringNS); //check webhook log for firing alert List pods = listPods(webhookNS, "app=webhook").getItems(); assertNotNull((pods), "No pods are running in namespace : " + webhookNS); @@ -402,7 +412,7 @@ private void fireAlert() throws ApiException { testUntil(withLongRetryPolicy, assertDoesNotThrow(() -> searchPodLogForKey(pod, - "Some WLS cluster has only one running server for more than 1 minutes"), + "Some WLS cluster has only one running server for more than 15 secs"), "webhook failed to fire alert"), logger, "webhook to fire alert"); @@ -554,7 +564,7 @@ public void tearDownAll() { deletePersistentVolumeClaim("pvc-" + grafanaReleaseName, monitoringNS); deletePersistentVolume("pv-test" + grafanaReleaseName); deleteNamespace(monitoringNS); - uninstallDeploymentService(webhookDepl, webhookService); + //uninstallDeploymentService(webhookDepl, webhookService); uninstallDeploymentService(coordinatorDepl, coordinatorService); if (nginxHelmParams != null) { assertThat(uninstallNginx(nginxHelmParams.getHelmParams())) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java index 814647ae072..f0ad418714c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/MonitoringUtils.java @@ -324,6 +324,33 @@ public static void checkMetricsViaPrometheus(String searchKey, String expectedVa expectedVal); } + /** + * Check metrics using Prometheus. + * + * @param expectedVal - expected alert data to search + * @param hostPortPrometheus host:nodePort for prometheus + * @throws Exception if command to check metrics fails + */ + public static void checkPrometheusAlert(String expectedVal, + String hostPortPrometheus, String ingressHost) + throws Exception { + + LoggingFacade logger = getLogger(); + // url + String curlCmd = + String.format("curl -g --silent --show-error --noproxy '*' -v -H 'host: " + ingressHost + "'" + + " http://%s/api/v1/alerts", + hostPortPrometheus); + + logger.info("Executing Curl cmd {0}", curlCmd); + logger.info(" expected Value {0} ", expectedVal); + testUntil( + searchForKey(curlCmd, expectedVal), + logger, + "Check prometheus alert against expected {0}", + expectedVal); + } + /** * Check output of the command against expected output. * diff --git a/integration-tests/src/test/resources/exporter/promvalues.yaml b/integration-tests/src/test/resources/exporter/promvalues.yaml index 72035806f38..5aa2fafccb8 100644 --- a/integration-tests/src/test/resources/exporter/promvalues.yaml +++ b/integration-tests/src/test/resources/exporter/promvalues.yaml @@ -143,11 +143,11 @@ serverFiles: rules: - alert: ClusterWarning for: 15s - expr: sum by(weblogic_domainUID, weblogic_clusterName) (up{weblogic_domainUID=~'.+'}) == 1 + expr: sum by(weblogic_domainUID, weblogic_clusterName) (up{weblogic_domainUID=~'.+', job="wls-domain1"}) == 1 labels: severity: page annotations: - description: 'Some WLS cluster has only one running server for more than 1 minutes.' + description: 'Some WLS cluster has only one running server for more than 15 secs.' summary: 'Some wls cluster is in warning state.' extraScrapeConfigs: | diff --git a/integration-tests/src/test/resources/exporter/promvaluesoke.yaml b/integration-tests/src/test/resources/exporter/promvaluesoke.yaml index c74a8ea83fc..fbb08e06f57 100644 --- a/integration-tests/src/test/resources/exporter/promvaluesoke.yaml +++ b/integration-tests/src/test/resources/exporter/promvaluesoke.yaml @@ -147,11 +147,11 @@ serverFiles: rules: - alert: ClusterWarning for: 15s - expr: sum by(weblogic_domainUID, weblogic_clusterName) (up{weblogic_domainUID=~'.+'}) == 1 + expr: sum by(weblogic_domainUID, weblogic_clusterName) (up{weblogic_domainUID=~'.+', job="wls-domain1"}) == 1 labels: severity: page annotations: - description: 'Some WLS cluster has only one running server for more than 1 minutes.' + description: 'Some WLS cluster has only one running server for more than 15 secs.' summary: 'Some wls cluster is in warning state.' extraScrapeConfigs: | From 71f688628be713f5d02ec795197150589b601132 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 15 Apr 2025 09:36:54 -0400 Subject: [PATCH 320/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 06204034617..a1190f41b7e 100644 --- a/pom.xml +++ b/pom.xml @@ -716,7 +716,7 @@ 4.12.0 3.11.0 1.80 - 5.12.1 + 5.12.2 5.7.1 1.7.0 1.3.2 @@ -729,7 +729,7 @@ 2.18.3 2.18.3 2.4 - 2.12.1 + 2.13.0 12.1.1 2.0.17 1.5.18 From 86dce087b2c91680a1e89464ef07511faf834388 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Wed, 16 Apr 2025 17:11:09 +0000 Subject: [PATCH 321/356] Backport ItServerStartPolicyConfigCluster nightly fix to release/4.2 --- .../ItServerStartPolicyConfigCluster.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 5838899cc90..ea9059db73e 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -116,19 +116,23 @@ public void beforeEach() { domainUid, domainNamespace); } - // Check configured cluster configuration is available - boolean isServerConfigured = - checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); - assertTrue(isServerConfigured, - "Could not find managed server from configured cluster"); - logger.info("Found managed server from configured cluster"); - // Check standalone server configuration is available boolean isStandaloneServerConfigured = checkManagedServerConfiguration("standalone-managed", domainNamespace, adminServerPodName); assertTrue(isStandaloneServerConfigured, "Could not find standalone managed server from configured cluster"); logger.info("Found standalone managed server configuration"); + + // Check configured cluster configuration is available + String configServerPodName = domainUid + "-config-cluster-server1"; + checkPodReadyAndServiceExists(configServerPodName, + domainUid, domainNamespace); + boolean isServerConfigured = + checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); + assertTrue(isServerConfigured, + "Could not find managed server from configured cluster"); + logger.info("Found managed server from configured cluster"); + } /** From ffe20d5fe0a3ad5376345c5b74386071d7c84912 Mon Sep 17 00:00:00 2001 From: xian_cao Date: Wed, 16 Apr 2025 22:20:19 +0000 Subject: [PATCH 322/356] add test order in ItIntrospectVersion.java --- .../weblogic/kubernetes/ItIntrospectVersion.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java index a850eb95fc5..f46aa0cc7e5 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIntrospectVersion.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -59,6 +59,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -292,6 +293,7 @@ public static void initAll(@Namespaces(3) List namespaces) { * under domain status. * Verifies that the new pod comes up and sample application deployment works. */ + @Order(1) @Test @DisplayName("Test introSpectVersion starting a introspector and updating domain status") @Tag("gate") @@ -419,6 +421,7 @@ void testDomainIntrospectVersionNotRolling() { * Verifies the new admin port of the admin server in services. * Verifies accessing sample application in admin server works. */ + @Order(2) @Test @DisplayName("Test introspectVersion rolling server pods when admin server port is changed") void testDomainIntrospectVersionRolling() throws ApiException { @@ -543,6 +546,7 @@ void testDomainIntrospectVersionRolling() throws ApiException { * e. Make a REST api call to access management console using new password. * f. Make a REST api call to access management console using old password. */ + @Order(3) @Test @DisplayName("Test change WebLogic admin credentials for domain running in persistent volume") void testCredentialChange() { @@ -678,6 +682,7 @@ void testCredentialChange() { * d. Verifies the servers in the new WebLogic cluster comes up without affecting any of the running servers on * pre-existing WebLogic cluster. */ + @Order(4) @Test @DisplayName("Test new cluster creation on demand using WLST and introspection") void testCreateNewCluster() { @@ -760,6 +765,7 @@ void testCreateNewCluster() { * Verify all the pods are restarted and back to ready state * Verify the admin server is accessible and cluster members are healthy */ + @Order(5) @Test @DisplayName("Verify server pods are restarted by updating image name") void testUpdateImageName() { @@ -865,6 +871,7 @@ void testUpdateImageName() { * Test that when a domain resource has spec.introspectVersion configured, * after a cluster is scaled up, new server pods have the label "weblogic.introspectVersion" set as well. */ + @Order(6) @Test @DisplayName("Scale up cluster-1 in domain1Namespace and verify label weblogic.introspectVersion set") void testDedicatedModeSameNamespaceScale() { @@ -926,6 +933,7 @@ void testDedicatedModeSameNamespaceScale() { * It also verifies the intospector job is started/stoped and none of the * server pod is rolled since there is no change to resource configuration. */ + @Order(7) @Test @DisplayName("Test to use sample scripts to explicitly initiate introspection") void testIntrospectDomainScript() { @@ -1066,6 +1074,7 @@ void testIntrospectDomainScript() { * type: Completed, status: false * Verify the introspector reruns to make it right when model file is fixed. */ + @Order(8) @Test @DisplayName("Test domain status condition with bad model file") void testIntrospectorMakeright() { From 847fe6f774c7c9cb5056610fdb78c1557bc87045 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 24 Apr 2025 15:55:37 -0400 Subject: [PATCH 323/356] Have monitoring exporter use configured container security --- .../java/oracle/kubernetes/operator/helpers/PodStepContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index 2bac7de476b..4fd17ba8967 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -1668,7 +1668,7 @@ private V1Container createMonitoringExporterContainer() { .image(getDomain().getMonitoringExporterImage()) .imagePullPolicy(getDomain().getMonitoringExporterImagePullPolicy()) .resources(getDomain().getMonitoringExporterResources()) - .securityContext(PodSecurityHelper.getDefaultContainerSecurityContext()) + .securityContext(getServerSpec().getContainerSecurityContext()) .addEnvItem(new V1EnvVar().name("JAVA_OPTS").value(createJavaOptions())) .addPortsItem(new V1ContainerPort() .name("metrics").protocol("TCP").containerPort(getPort())); From cf277eaaec9c0155cbd0fbf159d5040dfcc98b09 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 24 Apr 2025 15:59:05 -0400 Subject: [PATCH 324/356] Also update auxiliary image container security context --- .../oracle/kubernetes/operator/helpers/BasePodStepContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java index 9151571bcef..b0a488eec0e 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/BasePodStepContext.java @@ -194,7 +194,7 @@ protected V1Container createInitContainerForAuxiliaryImage(DeploymentImage auxil container.volumeMounts(mounts); if (isInitializeDomainOnPV) { - container.securityContext(PodSecurityHelper.getDefaultContainerSecurityContext()); + container.securityContext(getServerSpec().getContainerSecurityContext()); } else { container.securityContext(getInitContainerSecurityContext()); } From 75ef506ca58daeeb92c190b7639c4e551df1548a Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 24 Apr 2025 16:10:28 -0400 Subject: [PATCH 325/356] Update Fluentd and Fluentbit containers as well --- .../kubernetes/operator/helpers/FluentbitHelper.java | 9 ++++++--- .../kubernetes/operator/helpers/FluentdHelper.java | 9 ++++++--- .../kubernetes/operator/helpers/JobStepContext.java | 4 ++-- .../kubernetes/operator/helpers/PodStepContext.java | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java index 592067f87ef..9222b5d5073 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentbitHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2025, Oracle and/or its affiliates. +// Copyright (c) 2024, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -18,6 +18,7 @@ import io.kubernetes.client.openapi.models.V1VolumeMount; import oracle.kubernetes.operator.LabelConstants; import oracle.kubernetes.operator.LogHomeLayoutType; +import oracle.kubernetes.operator.processing.EffectiveServerSpec; import oracle.kubernetes.weblogic.domain.model.DomainResource; import oracle.kubernetes.weblogic.domain.model.FluentbitSpecification; @@ -35,12 +36,14 @@ private FluentbitHelper() { /** * Add sidecar container for fluentbit. + * @param serverSpec Server specification * @param fluentbitSpecification FluentbitSpecification. * @param containers List of containers. * @param isJobPod whether it belongs to the introspector job pod. * @param domain Domain. */ - public static void addFluentbitContainer(FluentbitSpecification fluentbitSpecification, List containers, + public static void addFluentbitContainer(EffectiveServerSpec serverSpec, + FluentbitSpecification fluentbitSpecification, List containers, DomainResource domain, boolean isJobPod, boolean isReadOnlyRootFileSystem) { V1Container fluentbitContainer = new V1Container(); @@ -57,7 +60,7 @@ public static void addFluentbitContainer(FluentbitSpecification fluentbitSpecifi fluentbitContainer.setImage(fluentbitSpecification.getImage()); fluentbitContainer.setImagePullPolicy(fluentbitSpecification.getImagePullPolicy()); fluentbitContainer.setResources(fluentbitSpecification.getResources()); - fluentbitContainer.setSecurityContext(PodSecurityHelper.getDefaultContainerSecurityContext()); + fluentbitContainer.setSecurityContext(serverSpec.getContainerSecurityContext()); if (fluentbitSpecification.getContainerCommand() != null) { fluentbitContainer.setCommand(fluentbitSpecification.getContainerCommand()); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java index e5d9c3dfdc2..9dd188bede5 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/FluentdHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -18,6 +18,7 @@ import io.kubernetes.client.openapi.models.V1VolumeMount; import oracle.kubernetes.operator.LabelConstants; import oracle.kubernetes.operator.LogHomeLayoutType; +import oracle.kubernetes.operator.processing.EffectiveServerSpec; import oracle.kubernetes.weblogic.domain.model.DomainResource; import oracle.kubernetes.weblogic.domain.model.FluentdSpecification; @@ -35,12 +36,14 @@ private FluentdHelper() { /** * Add sidecar container for fluentd. + * @param serverSpec Server specification * @param fluentdSpecification FluentdSpecification. * @param containers List of containers. * @param isJobPod whether it belongs to the introspector job pod. * @param domain Domain. */ - public static void addFluentdContainer(FluentdSpecification fluentdSpecification, List containers, + public static void addFluentdContainer(EffectiveServerSpec serverSpec, + FluentdSpecification fluentdSpecification, List containers, DomainResource domain, boolean isJobPod, boolean isReadOnlyRootFileSystem) { V1Container fluentdContainer = new V1Container(); @@ -59,7 +62,7 @@ public static void addFluentdContainer(FluentdSpecification fluentdSpecification fluentdContainer.setImage(fluentdSpecification.getImage()); fluentdContainer.setImagePullPolicy(fluentdSpecification.getImagePullPolicy()); fluentdContainer.setResources(fluentdSpecification.getResources()); - fluentdContainer.setSecurityContext(PodSecurityHelper.getDefaultContainerSecurityContext()); + fluentdContainer.setSecurityContext(serverSpec.getContainerSecurityContext()); if (fluentdSpecification.getContainerCommand() != null) { fluentdContainer.setCommand(fluentdSpecification.getContainerCommand()); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java index 4397655b505..12313e8971b 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobStepContext.java @@ -841,7 +841,7 @@ protected List getContainers() { Optional.ofNullable(getDomain().getFluentdSpecification()) .ifPresent(fluentd -> { if (Boolean.TRUE.equals(fluentd.getWatchIntrospectorLogs())) { - FluentdHelper.addFluentdContainer(fluentd, + FluentdHelper.addFluentdContainer(getServerSpec(), fluentd, containers, getDomain(), true, isReadOnlyRootFileSystem()); } }); @@ -849,7 +849,7 @@ protected List getContainers() { Optional.ofNullable(getDomain().getFluentbitSpecification()) .ifPresent(fluentbit -> { if (Boolean.TRUE.equals(fluentbit.getWatchIntrospectorLogs())) { - FluentbitHelper.addFluentbitContainer(fluentbit, + FluentbitHelper.addFluentbitContainer(getServerSpec(), fluentbit, containers, getDomain(), true, isReadOnlyRootFileSystem()); } }); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index 4fd17ba8967..7b0daae15df 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -784,10 +784,10 @@ protected List getContainers() { exporterContext.addContainer(containers); boolean isReadOnlyRootFileSystem = isReadOnlyRootFileSystem(); Optional.ofNullable(getDomain().getFluentdSpecification()) - .ifPresent(fluentd -> addFluentdContainer(fluentd, containers, getDomain(), false, + .ifPresent(fluentd -> addFluentdContainer(getServerSpec(), fluentd, containers, getDomain(), false, isReadOnlyRootFileSystem)); Optional.ofNullable(getDomain().getFluentbitSpecification()) - .ifPresent(fluentbit -> addFluentbitContainer(fluentbit, containers, getDomain(), + .ifPresent(fluentbit -> addFluentbitContainer(getServerSpec(), fluentbit, containers, getDomain(), false, isReadOnlyRootFileSystem)); return containers; } From 57a3e06c984a84a27dae8f548858de4926c6709d Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 28 Apr 2025 15:18:49 +0000 Subject: [PATCH 326/356] Merge branch 'rm-update-OpenShift-support' into 'main' update OpenShift support See merge request weblogic-cloud/weblogic-kubernetes-operator!4956 (cherry picked from commit b5b30fad228f23910ac9dcf9a961dd4e94cf0824) 823d6444 update OpenShift support --- .../site/content/introduction/platforms/environments.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/site/content/introduction/platforms/environments.md b/documentation/site/content/introduction/platforms/environments.md index 1a1ee418e8b..50dc4bfb7cd 100644 --- a/documentation/site/content/introduction/platforms/environments.md +++ b/documentation/site/content/introduction/platforms/environments.md @@ -179,6 +179,8 @@ OpenShift can be a cloud platform or can be deployed on premises. - Operator v4.0.5 is certified for use on: - OpenShift Container Platform 4.11.30 with Kubernetes 1.24+, RedHat OpenShift Mesh 2.3.2, and Istio 1.14.5. - OpenShift Container Platform 4.12.2 with Kubernetes 1.25+, RedHat OpenShift Mesh 2.3.2, and Istio 1.14.5. +- Operator v4.2.16 is certified for use on: + - OpenShift Container Platform 4.17.2 with Kubernetes v1.30.5+, RedHat OpenShift Mesh v3.0.1, and Istio 1.24.4. To accommodate OpenShift security requirements: - For security requirements to run WebLogic Server in OpenShift, see the [OpenShift]({{}}) documentation. From 2790d339088adcea3d144876309b0020979b4e9a Mon Sep 17 00:00:00 2001 From: huiling_zhao Date: Mon, 28 Apr 2025 18:03:38 +0000 Subject: [PATCH 327/356] ItMaxConcurOptions keeps failing on wko-kind-release42-nightly-podman --- .../weblogic/kubernetes/ItMaxConcurOptions.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java index 47888f9ba03..e0982b8afd4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItMaxConcurOptions.java @@ -24,6 +24,7 @@ import oracle.weblogic.domain.DomainResource; import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.Model; +import oracle.weblogic.domain.ProbeTuning; import oracle.weblogic.domain.ServerPod; import oracle.weblogic.domain.ServerService; import oracle.weblogic.kubernetes.actions.impl.OperatorParams; @@ -442,10 +443,19 @@ private static DomainResource createDomainResource(String domainUid, .serverPod(new ServerPod() .addEnvItem(new V1EnvVar() .name("JAVA_OPTIONS") - .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true")) + .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true -Xms1024m -Xmx1024m")) .addEnvItem(new V1EnvVar() .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom "))) + .value("-Djava.security.egd=file:/dev/./urandom ")) + .addEnvItem(new V1EnvVar() + .name("MEM_ARGS") + .value("-Xms1024m -Xmx1024m")) + .livenessProbe(new ProbeTuning() + .initialDelaySeconds(300) + .failureThreshold(2)) + .readinessProbe(new ProbeTuning() + .initialDelaySeconds(300) + .failureThreshold(2))) .adminServer(new AdminServer() .adminChannelPortForwardingEnabled(false) .serverService(new ServerService() From 4478a74e6a671b2b637d1d834a8a938330bc6a8a Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Mon, 5 May 2025 19:49:10 +0000 Subject: [PATCH 328/356] Added tests for readOnlyRootFilesystem (OWLS-127977) back port to RELEASE 4.2 --- .../weblogic/kubernetes/ItReadOnlyRootFS.java | 651 ++++++++++++++++++ .../kubernetes/utils/ConfigMapUtils.java | 32 +- 2 files changed, 680 insertions(+), 3 deletions(-) create mode 100644 integration-tests/src/test/java/oracle/weblogic/kubernetes/ItReadOnlyRootFS.java diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItReadOnlyRootFS.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItReadOnlyRootFS.java new file mode 100644 index 00000000000..02ebd6bb504 --- /dev/null +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItReadOnlyRootFS.java @@ -0,0 +1,651 @@ +// Copyright (c) 2025, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.weblogic.kubernetes; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1Container; +import io.kubernetes.client.openapi.models.V1EmptyDirVolumeSource; +import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimVolumeSource; +import io.kubernetes.client.openapi.models.V1Pod; +import io.kubernetes.client.openapi.models.V1PodList; +import io.kubernetes.client.openapi.models.V1SecurityContext; +import io.kubernetes.client.openapi.models.V1Volume; +import io.kubernetes.client.openapi.models.V1VolumeMount; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.domain.DomainSpec; +import oracle.weblogic.domain.FluentdSpecification; +import oracle.weblogic.domain.MonitoringExporterSpecification; +import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.AppParams; +import oracle.weblogic.kubernetes.annotations.IntegrationTest; +import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.logging.LoggingFacade; +import oracle.weblogic.kubernetes.utils.ExecResult; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.ELASTICSEARCH_HTTP_PORT; +import static oracle.weblogic.kubernetes.TestConstants.FLUENTD_IMAGE; +import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; +import static oracle.weblogic.kubernetes.TestConstants.MII_AUXILIARY_IMAGE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_WDT_MODEL_FILE; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; +import static oracle.weblogic.kubernetes.TestConstants.RESULTS_TEMPFILE; +import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TO_USE_IN_SPEC; +import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.RESOURCE_DIR; +import static oracle.weblogic.kubernetes.actions.TestActions.buildAppArchive; +import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; +import static oracle.weblogic.kubernetes.actions.TestActions.deleteDomainCustomResource; +import static oracle.weblogic.kubernetes.actions.TestActions.execCommand; +import static oracle.weblogic.kubernetes.actions.TestActions.listPods; +import static oracle.weblogic.kubernetes.utils.AuxiliaryImageUtils.createPushAuxiliaryImageWithDomainConfig; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getImageBuilderExtraArgs; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getNextFreePort; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.getUniqueName; +import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapForDomainCreation; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; +import static oracle.weblogic.kubernetes.utils.IstioUtils.createAdminServer; +import static oracle.weblogic.kubernetes.utils.JobUtils.createDomainJob; +import static oracle.weblogic.kubernetes.utils.MonitoringUtils.buildMonitoringExporterCreateImageAndPushToRepo; +import static oracle.weblogic.kubernetes.utils.MonitoringUtils.installMonitoringExporter; +import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPV; +import static oracle.weblogic.kubernetes.utils.PersistentVolumeUtils.createPVC; +import static oracle.weblogic.kubernetes.utils.PodUtils.getPodName; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePasswordElk; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +@DisplayName("Verify /tmp is mounted as tmpfs across containers in PV domains and " + + "readOnlyRootFilesystem flag enabled in its security context in each container") +@IntegrationTest +@Tag("kind-parallel") +@Tag("oke-parallel") +class ItReadOnlyRootFS { + + private static String opNamespace; + private static String domainNamespace; + private final String wlSecretName = "weblogic-credentials"; + private static LoggingFacade logger; + final String managedServerNameBase = "managed-"; + + private final String adminServerName = "admin-server"; + private static String exporterImage = null; + final int replicaCount = 2; + private static final String FLUENTD_CONFIGMAP_YAML = "fluentd.configmap.elk.yaml"; + private final List domainsToClean = new ArrayList<>(); + + + @BeforeAll + public static void initAll(@Namespaces(2) List namespaces) { + logger = getLogger(); + opNamespace = namespaces.get(0); + domainNamespace = namespaces.get(1); + installAndVerifyOperator(opNamespace, domainNamespace); + String monitoringExporterDir = Paths.get(RESULTS_ROOT, + "ItReadOnlyRootFS", "monitoringexp",domainNamespace).toString(); + logger.info("install monitoring exporter"); + installMonitoringExporter(monitoringExporterDir); + String monitoringExporterSrcDir = Paths.get(monitoringExporterDir, "srcdir").toString(); + exporterImage = assertDoesNotThrow(() -> + buildMonitoringExporterCreateImageAndPushToRepo(monitoringExporterSrcDir, "exporter", + domainNamespace, TEST_IMAGES_REPO_SECRET_NAME, getImageBuilderExtraArgs()), + "Failed to create image for exporter"); + } + + @AfterEach + public void cleanupDomains() { + for (String domainUid : domainsToClean) { + logger.info("Cleaning up domain {0} in namespace {1}", domainUid, domainNamespace); + deleteDomainCustomResource(domainUid, domainNamespace); + } + domainsToClean.clear(); // Reset for next test + } + + @Test + @DisplayName("fluentd with exporter") + void testFluentdWithExporter() { + logger.info("Starting test: fluentd with exporter"); + assertDoesNotThrow(() -> runDomainWithOptions("fluentd", true)); + logger.info("Finished test: fluentd with exporter"); + } + + @Test + @DisplayName("fluentd without exporter") + void testFluentdWithoutExporter() { + logger.info("Starting test: fluentd without exporter"); + assertDoesNotThrow(() -> runDomainWithOptions("fluentd", false)); + logger.info("Finished test: fluentd without exporter"); + } + + @Test + @DisplayName("fluentbit with exporter") + void testFluentbitWithExporter() { + logger.info("Starting test: fluentbit with exporter"); + assertDoesNotThrow(() -> runDomainWithOptions("fluentbit", true)); + logger.info("Finished test: fluentbit with exporter"); + } + + @Test + @DisplayName("fluentbit without exporter") + void testFluentbitWithoutExporter() { + logger.info("Starting test: fluentbit without exporter"); + assertDoesNotThrow(() -> runDomainWithOptions("fluentbit", false)); + logger.info("Finished test: fluentbit without exporter"); + } + + @Test + @DisplayName("no logging with exporter") + void testNoLoggingWithExporter() { + logger.info("Starting test: no logging with exporter"); + assertDoesNotThrow(() -> runDomainWithOptions("none", true)); + logger.info("Finished test: no logging with exporter"); + } + + @Test + @DisplayName("no logging without exporter") + void testNoLoggingWithoutExporter() { + logger.info("Starting test: no logging without exporter"); + assertDoesNotThrow(() -> runDomainWithOptions("none", false)); + logger.info("Finished test: no logging without exporter"); + } + + @Test + @DisplayName("auxiliary init container with readOnlyRootFilesystem and emptyDir domainHome") + void testAuxiliaryInitContainerWithReadOnlyFSOnEmptyDir() throws Exception { + logger.info("Starting test: auxiliary init container with readOnlyRootFilesystem and emptyDir domainHome"); + + String domainUid = "auxemptyreadonly"; + String domainHomeBase = "/u02"; + String domainHome = domainHomeBase + "/domains/" + domainUid; + String adminServerPodName = domainUid + "-admin-server"; + String managedServerPodPrefix = domainUid + "-managed-server"; + + // Prepare secrets + createBaseRepoSecret(domainNamespace); + createSecretWithUsernamePassword(wlSecretName + domainUid, domainNamespace, + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + + // Build and push auxiliary image + String auxImageName = buildAndPushAuxiliaryImage(); + + // Create domain resource with auxiliary image and readonly FS + DomainResource domain = createDomainResourceWithAuxiliaryImageAndReadOnlyFS( + domainUid, domainHomeBase, domainHome, auxImageName); + + setPodAntiAffinity(domain); + createDomainAndTrackForCleanup(domain, domainNamespace); + + // Check pod readiness + checkPodReadyAndServiceExists(adminServerPodName, domainUid, domainNamespace); + for (int i = 1; i <= replicaCount; i++) { + checkPodReadyAndServiceExists(managedServerPodPrefix + i, domainUid, domainNamespace); + } + + logger.info("Verifying /tmp mounted as tmpfs and readOnly filesystem for all containers including auxiliary"); + verifyAllPodsTmpfsAndReadOnlyFS(domainNamespace, domainUid); + } + + private String buildAndPushAuxiliaryImage() throws IOException { + logger.info("Building auxiliary image for test"); + + List modelFiles = List.of( + MODEL_DIR + "/" + MII_BASIC_WDT_MODEL_FILE, + MODEL_DIR + "/multi-model-one-ds.20.yaml" + ); + + String appDir = "sample-app"; + AppParams appParams = defaultAppParams() + .appArchiveDir(ARCHIVE_DIR + ItReadOnlyRootFS.class.getSimpleName()); + + assertTrue( + buildAppArchive(appParams.srcDirList(List.of(appDir))), + String.format("Failed to create application archive for %s", MII_BASIC_APP_NAME) + ); + + String archiveFile = String.format("%s/%s.zip", appParams.appArchiveDir(), MII_BASIC_APP_NAME); + List archiveFiles = Collections.singletonList(archiveFile); + + String auxImageTag = MII_BASIC_IMAGE_TAG; + String auxImageName = MII_AUXILIARY_IMAGE_NAME + ":" + auxImageTag; + + assertDoesNotThrow(() -> + createPushAuxiliaryImageWithDomainConfig(MII_AUXILIARY_IMAGE_NAME, auxImageTag, archiveFiles, modelFiles), + "Failed to create auxiliary image"); + + return auxImageName; + } + + private DomainResource createDomainResourceWithAuxiliaryImageAndReadOnlyFS( + String domainUid, String domainHomeBase, String domainHome, String auxImageName) { + + return new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new V1ObjectMeta() + .name(domainUid) + .namespace(domainNamespace)) + .spec(new DomainSpec() + .domainUid(domainUid) + .domainHome(domainHome) + .domainHomeSourceType("FromModel") + .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) + .imagePullPolicy(IMAGE_PULL_POLICY) + .replicas(replicaCount) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(wlSecretName + domainUid)) + .includeServerOutInPodLog(true) + .logHomeEnabled(true) + .logHome(domainHomeBase + "/logs/" + domainUid) + .serverStartPolicy("IfNeeded") + .serverPod(buildServerPod(domainHomeBase)) + .configuration(buildConfiguration(auxImageName, wlSecretName + domainUid))); + } + + private ServerPod buildServerPod(String domainHomeBase) { + return new ServerPod() + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true " + + "-Dweblogic.StdoutDebugEnabled=false")) + .addEnvItem(new V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom")) + .containerSecurityContext(new V1SecurityContext() + .readOnlyRootFilesystem(true)) + .addVolumesItem(new V1Volume() + .name("domain-dir") + .emptyDir(new V1EmptyDirVolumeSource())) + .addVolumeMountsItem(new V1VolumeMount() + .mountPath(domainHomeBase) + .name("domain-dir")); + } + + private Configuration buildConfiguration(String auxImageName, String runtimeEncryptionSecretName) { + return new Configuration() + .model(new oracle.weblogic.domain.Model() + .domainType("WLS") + .runtimeEncryptionSecret(runtimeEncryptionSecretName) + .withAuxiliaryImage(new oracle.weblogic.domain.AuxiliaryImage() + .image(auxImageName) + .imagePullPolicy(IMAGE_PULL_POLICY) + .sourceWDTInstallHome("/auxiliary/weblogic-deploy") + .sourceModelHome("/auxiliary/models"))); + } + + + private void runDomainWithOptions(String logType, boolean exporterEnabled) + throws IOException, ApiException { + logger.info("Running domain test with logType: {0}, exporterEnabled: {1}", logType, exporterEnabled); + String testSuffix = logType + (exporterEnabled ? "exp" : "noexp"); + String domainUid = "dpv" + testSuffix; + + String adminServerPodName = domainUid + "-" + adminServerName; + String managedServerPodNamePrefix = domainUid + "-" + managedServerNameBase; + + String pvName = getUniqueName(domainUid + "-pv"); + String pvcName = getUniqueName(domainUid + "-pvc"); + + createBaseRepoSecret(domainNamespace); + + if ("fluentd".equals(logType)) { + logger.info("Create secret for admin credentials"); + + String elasticSearchHost = "elasticsearch." + domainNamespace + ".svc"; + assertDoesNotThrow(() -> createSecretWithUsernamePasswordElk(wlSecretName + domainUid, domainNamespace, + ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT, + elasticSearchHost, String.valueOf(ELASTICSEARCH_HTTP_PORT)), + String.format("create secret for admin credentials failed for %s", wlSecretName + domainUid)); + } else { + createSecretWithUsernamePassword(wlSecretName + domainUid, + domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + } + + createPV(pvName, domainUid, this.getClass().getSimpleName()); + createPVC(pvName, pvcName, domainUid, domainNamespace); + + File domainPropsFile = createDomainProperties(domainUid); + Path wlstScript = Paths.get(RESOURCE_DIR, "python-scripts", "sit-config-create-domain.py"); + createDomainOnPVUsingWlst(wlstScript, domainPropsFile.toPath(), pvName, pvcName, domainNamespace, domainUid); + + DomainResource domain = buildDomainResource(domainUid, pvName, pvcName, logType, exporterEnabled); + setPodAntiAffinity(domain); + + createDomainAndTrackForCleanup(domain, domainNamespace); + // verify the admin server service created + checkPodReadyAndServiceExists(adminServerPodName, domainUid, domainNamespace); + + // verify managed server services created + for (int i = 1; i <= replicaCount; i++) { + logger.info("Checking managed service {0} is created in namespace {1}", + managedServerPodNamePrefix + i, domainNamespace); + checkPodReadyAndServiceExists(managedServerPodNamePrefix + i, domainUid, domainNamespace); + + } + + logger.info("Domain {0} deployed and verified successfully", domainUid); + verifyAllPodsTmpfsAndReadOnlyFS(domainNamespace, domainUid); + } + + private File createDomainProperties(String domainUid) { + try { + File props = assertDoesNotThrow(() -> + File.createTempFile("domain-" + domainUid, ".properties", new File(RESULTS_TEMPFILE)), + "Failed to create domain properties file"); + Properties p = new Properties(); + + // Base domain properties + p.setProperty("domain_path", "/shared/" + domainNamespace + "/domains"); + p.setProperty("domain_name", domainUid); + p.setProperty("domain_uid", domainUid); + p.setProperty("cluster_name", "cluster-1"); + p.setProperty("admin_server_name", "admin-server"); + p.setProperty("managed_server_port", "8001"); + p.setProperty("admin_server_port", "7001"); + p.setProperty("admin_username", ADMIN_USERNAME_DEFAULT); + p.setProperty("admin_password", ADMIN_PASSWORD_DEFAULT); + p.setProperty("admin_t3_public_address", K8S_NODEPORT_HOST); + p.setProperty("admin_t3_channel_port", Integer.toString(getNextFreePort())); + p.setProperty("number_of_ms", "2"); + p.setProperty("managed_server_name_base", "managed-"); + p.setProperty("domain_logs", "/shared/" + domainNamespace + "/logs/" + domainUid); + p.setProperty("production_mode_enabled", "true"); + + try (FileOutputStream fos = new FileOutputStream(props)) { + p.store(fos, "WLST domain creation properties"); + } + + return props; + + } catch (Exception e) { + throw new RuntimeException("Failed to create domain properties file for " + domainUid, e); + } + } + + + private DomainResource buildDomainResource(String domainUid, String pvName, String pvcName, + String logType, boolean exporterEnabled) { + V1SecurityContext roContext = new V1SecurityContext().readOnlyRootFilesystem(true); + FluentdSpecification fluentdSpec = null; + MonitoringExporterSpecification monitoringExporterSpec = null; + + V1Volume tmpfsVol = new V1Volume() + .name("memory-tmp") + .emptyDir(new V1EmptyDirVolumeSource().medium("Memory")); + V1VolumeMount tmpfsMount = new V1VolumeMount() + .mountPath("/memory-tmp") + .name("memory-tmp"); + + List sidecars = new ArrayList<>(); + if ("fluentd".equals(logType)) { + logger.info("Choosen FLUENTD_IMAGE {0}", FLUENTD_IMAGE); + String imagePullPolicy = "IfNotPresent"; + FluentdSpecification fluentdSpecification = new FluentdSpecification(); + fluentdSpecification.setImage(FLUENTD_IMAGE); + fluentdSpecification.setWatchIntrospectorLogs(true); + fluentdSpecification.setImagePullPolicy(imagePullPolicy); + fluentdSpecification.setElasticSearchCredentials("weblogic-credentials" + domainUid); + V1VolumeMount fluentdLogMount = new V1VolumeMount() + .mountPath("/memory-tmp/logs") // or wherever Fluentd writes logs + .name("memory-tmp"); + fluentdSpecification.setVolumeMounts(List.of(tmpfsMount, fluentdLogMount)); + + assertDoesNotThrow(() -> { + Path filePath = Path.of(MODEL_DIR + "/" + FLUENTD_CONFIGMAP_YAML); + fluentdSpecification.setFluentdConfiguration(Files.readString(filePath)); + }); + fluentdSpec = fluentdSpecification; + + } else if ("fluentbit".equals(logType)) { + sidecars.add(new V1Container() + .name("fluentbit") + .image("fluent/fluent-bit:latest") + .securityContext(roContext) + .volumeMounts(List.of(tmpfsMount))); + } + if (exporterEnabled) { + String monexpConfigFile = RESOURCE_DIR + "/exporter/rest_webapp.yaml"; + logger.info("YAML config file path: {}", monexpConfigFile); + + String contents; + try { + contents = Files.readString(Paths.get(monexpConfigFile)); + monitoringExporterSpec = new MonitoringExporterSpecification() + .image(exporterImage) + .imagePullPolicy(IMAGE_PULL_POLICY) + .configuration(contents); + } catch (IOException e) { + logger.severe("Failed to read monitoring exporter config file: {0}", e.getMessage()); + throw new RuntimeException("Unable to read monitoring exporter config", e); + + } + } + DomainSpec spec = new DomainSpec() + .domainUid(domainUid) + .domainHome("/shared/" + domainNamespace + "/domains/" + domainUid) + .domainHomeSourceType("PersistentVolume") + .image(WEBLOGIC_IMAGE_TO_USE_IN_SPEC) + .imagePullPolicy(IMAGE_PULL_POLICY) + .replicas(2) + .imagePullSecrets(List.of(new V1LocalObjectReference().name(BASE_IMAGES_REPO_SECRET_NAME))) + .webLogicCredentialsSecret(new V1LocalObjectReference().name(wlSecretName + domainUid)) + .includeServerOutInPodLog(true) + .logHomeEnabled(true) + .logHome("/shared/" + domainNamespace + "/logs/" + domainUid) + .dataHome("") + .serverStartPolicy("IfNeeded") + .serverPod(new ServerPod() + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true")) + .addEnvItem(new io.kubernetes.client.openapi.models.V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false")) + .addEnvItem(new io.kubernetes.client.openapi.models.V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom ")) + .containerSecurityContext(roContext) + .addVolumesItem(new V1Volume().name(pvName) + .persistentVolumeClaim(new V1PersistentVolumeClaimVolumeSource().claimName(pvcName))) + .addVolumeMountsItem(new V1VolumeMount().mountPath("/shared").name(pvName)) + .addVolumesItem(tmpfsVol) + .addVolumeMountsItem(tmpfsMount) + .addVolumeMountsItem(new V1VolumeMount() + .name("memory-tmp") + .mountPath("/memory-tmp/logs"))) + .adminServer(createAdminServer()) + .configuration(new Configuration()); + if (fluentdSpec != null) { + spec.withFluentdConfiguration(fluentdSpec); + } + if (monitoringExporterSpec != null) { + spec.monitoringExporter(monitoringExporterSpec); + } + return new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new V1ObjectMeta().name(domainUid).namespace(domainNamespace)) + .spec(spec); + + } + + public static void verifyAllPodsTmpfsAndReadOnlyFS(String domainNamespace, String domainUid) throws ApiException { + List failures = new ArrayList<>(); + + V1PodList podList = listPods(domainNamespace, String.format("weblogic.domainUID in (%s)", domainUid)); + List pods = podList.getItems(); + + for (V1Pod pod : pods) { + String podName = pod.getMetadata().getName(); + logger.info("Checking pod: {0}", podName); + + // Check init containers + List initContainers = pod.getSpec().getInitContainers(); + if (initContainers != null) { + for (V1Container initContainer : initContainers) { + logger.info("Checking init container: {0}", initContainer.getName()); + validateContainerSpec(initContainer, podName, domainNamespace, true, failures); + } + } + + // Check normal containers + List containers = pod.getSpec().getContainers(); + for (V1Container container : containers) { + logger.info("Checking container: {0}", container.getName()); + validateContainerSpec(container, podName, domainNamespace, false, failures); + } + } + + if (!failures.isEmpty()) { + failures.forEach(logger::severe); + fail("Some containers failed /tmp mount or readOnlyRootFilesystem checks: " + failures.size() + " failures"); + } else { + logger.info("All containers passed /tmp mount and readOnlyRootFilesystem checks"); + } + } + + + private static void validateContainerSpec(V1Container container, String podName, String namespace, + boolean isInitContainer, List failures) { + String containerName = container.getName(); + + // 1. Check securityContext.readOnlyRootFilesystem + V1SecurityContext securityContext = container.getSecurityContext(); + if (securityContext == null || !Boolean.TRUE.equals(securityContext.getReadOnlyRootFilesystem())) { + String msg = "FAIL: Container " + containerName + + " in pod " + podName + " does not have readOnlyRootFilesystem=true"; + logger.severe(msg); + failures.add(msg); + } else { + logger.info("PASS: Container " + containerName + " in pod " + podName + " has readOnlyRootFilesystem=true"); + } + + if (!isInitContainer) { + // 2. For regular containers, exec to check /tmp mount + try { + + ExecResult result = execCommand(namespace, podName, containerName, true, "df", "-h", "/tmp"); + String stdout = result.stdout(); + if (stdout == null || !stdout.contains("tmpfs")) { + String msg = "FAIL: /tmp is not mounted as tmpfs in container " + containerName + " in pod " + podName; + logger.severe(msg); + failures.add(msg); + } else { + logger.info("PASS: /tmp is mounted as tmpfs in container " + containerName + " in pod " + podName); + } + } catch (Exception e) { + String msg = "FAIL: Exec failed for container " + containerName + " in pod " + podName + ": " + e.getMessage(); + logger.severe(msg); + failures.add(msg); + } + } else { + // 3. For init container, check volumeMounts + List volumeMounts = container.getVolumeMounts(); + boolean hasTmpMount = volumeMounts != null && volumeMounts.stream() + .anyMatch(mount -> mount.getMountPath() != null && mount.getMountPath().startsWith("/tmp")); + if (!hasTmpMount) { + String msg = "FAIL: Init container " + containerName + " in pod " + podName + " does not have /tmp mounted"; + logger.severe(msg); + failures.add(msg); + } else { + logger.info("PASS: Init container " + containerName + " in pod " + podName + " has /tmp mounted"); + } + } + } + + private void verifyTmpfsAndSecurityContext(String podName, V1Container container) + throws IOException, ApiException, InterruptedException { + String containerName = container.getName(); + logger.info("Verifying /tmp mount in pod: {0}, container: {1}", podName, containerName); + + ExecResult result = execCommand(domainNamespace, podName, containerName, true, "df", "-h", "/tmp"); + logger.info("Output for pod: {0}, container: {1} for df -h /tmp : {2}", + podName, containerName, result.stdout()); + if (!result.stdout().contains("tmpfs")) { + Path logDir = Paths.get(RESULTS_TEMPFILE, podName); + Files.createDirectories(logDir); + Path logFile = logDir.resolve(containerName + "-tmp-check.log"); + Files.writeString(logFile, result.stdout()); + logger.severe("/tmp not mounted as tmpfs in container {0}. Log saved to: {1}", containerName, logFile); + } + assertTrue(result.stdout().contains("tmpfs"), + "/tmp is not tmpfs in container " + containerName + " of pod " + podName); + + V1SecurityContext context = container.getSecurityContext(); + assertTrue(context != null && Boolean.TRUE.equals(context.getReadOnlyRootFilesystem()), + "readOnlyRootFilesystem not set to true for container " + containerName + " in pod " + podName); + } + + private void createDomainOnPVUsingWlst(Path wlstScriptFile, Path domainPropertiesFile, + String pvName, String pvcName, + String namespace, String domainUid) throws IOException, ApiException { + List files = List.of(wlstScriptFile, domainPropertiesFile); + String cmName = "create-domain-scripts-cm"; + createConfigMapForDomainCreation(cmName, files, namespace, domainUid, this.getClass().getSimpleName()); + V1Container jobContainer = new V1Container() + .addCommandItem("/bin/sh") + .addArgsItem("/u01/oracle/oracle_common/common/bin/wlst.sh") + .addArgsItem("/u01/weblogic/" + wlstScriptFile.getFileName()) + .addArgsItem("-skipWLSModuleScanning") + .addArgsItem("-loadProperties") + .addArgsItem("/u01/weblogic/" + domainPropertiesFile.getFileName()); + Map annotations = Map.of("sidecar.istio.io/inject", "false"); + createDomainJob(WEBLOGIC_IMAGE_TO_USE_IN_SPEC, pvName, pvcName, cmName, namespace, jobContainer, annotations); + } + + private static List listPodNames(String ns, String domainUid) { + List podNames = new ArrayList<>(); + try { + // Add admin server pod + podNames.add(getPodName(ns, domainUid + "-admin-server")); + // Add managed server pods: assuming 2 replicas + for (int i = 1; i <= 2; i++) { + podNames.add(getPodName(ns, domainUid + "-managed-" + i)); + } + } catch (Exception e) { + throw new RuntimeException("Failed to get pod names for domain: " + domainUid, e); + } + return podNames; + } + + private void createDomainAndTrackForCleanup(DomainResource domain, String namespace) { + createDomainAndVerify(domain, namespace); + domainsToClean.add(domain.getMetadata().getName()); + } +} diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java index e4f7dae700c..8a966233ef7 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ConfigMapUtils.java @@ -126,11 +126,37 @@ public static void createConfigMapForDomainCreation(String configMapName, List

    files, + String namespace, String domainUid, String className) + throws ApiException, IOException { + LoggingFacade logger = getLogger(); - logger.info("Creating configmap {0}, namespace {1}, className {2}", configMapName, namespace, className); + logger.info("Creating configmap {0}, namespace {1}, domainUid {2}, className {3}", + configMapName, namespace, domainUid, className); + + Path domainScriptsDir; - Path domainScriptsDir = Files.createDirectories( - Paths.get(TestConstants.LOGS_DIR, className, namespace)); + if (domainUid == null || domainUid.isEmpty()) { + domainScriptsDir = Files.createDirectories( + Paths.get(TestConstants.LOGS_DIR, className, namespace)); + } else { + domainScriptsDir = Files.createDirectories( + Paths.get(TestConstants.LOGS_DIR, className, namespace, domainUid)); + } // add domain creation scripts and properties files to the configmap Map data = new HashMap<>(); From a4c08d7b2b9daeba7a5496113cd67da4498d4c1e Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Wed, 7 May 2025 21:34:44 +0000 Subject: [PATCH 329/356] Openshift istio test --- Jenkinsfile.openshift | 8 +- .../weblogic/kubernetes/ItIstioMiiDomain.java | 93 ++++++++++++++----- .../kubernetes/actions/TestActions.java | 16 +++- .../kubernetes/actions/impl/Namespace.java | 31 ++++++- .../istio/openshift-istio-roles-template.yaml | 83 +++++++++++++++++ 5 files changed, 202 insertions(+), 29 deletions(-) create mode 100644 integration-tests/src/test/resources/istio/openshift-istio-roles-template.yaml diff --git a/Jenkinsfile.openshift b/Jenkinsfile.openshift index 41c1d9fd661..7cc99939a0c 100644 --- a/Jenkinsfile.openshift +++ b/Jenkinsfile.openshift @@ -3,9 +3,9 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // -CRON_SETTINGS = '''H 1 * * * % MAVEN_PROFILE_NAME=wko-okd-wls-srg - H 2 * * * % MAVEN_PROFILE_NAME=wko-okd-wls-mrg - H 3 * * * % MAVEN_PROFILE_NAME=wko-okd-fmw-cert''' +CRON_SETTINGS = '''H 1 * * 0 % MAVEN_PROFILE_NAME=wko-okd-wls-srg + H 3 * * 0 % MAVEN_PROFILE_NAME=wko-okd-wls-mrg + H 5 * * 0 % MAVEN_PROFILE_NAME=wko-okd-fmw-cert''' pipeline { agent { label 'large' } @@ -15,7 +15,7 @@ pipeline { } triggers { // timer trigger for "nightly build" - parameterizedCron(env.JOB_NAME == 'wko-release42-nightly-okd' ? + parameterizedCron(env.JOB_NAME == 'openshift-release42-weekly' ? CRON_SETTINGS : '') } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java index d97b79c77af..47336d6f493 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItIstioMiiDomain.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -45,6 +45,7 @@ import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.OCNE; +import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.OKE_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; @@ -65,6 +66,7 @@ import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withStandardRetryPolicy; import static oracle.weblogic.kubernetes.utils.ConfigMapUtils.createConfigMapAndVerify; import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.ExecCommand.exec; import static oracle.weblogic.kubernetes.utils.FileUtils.generateFileFromTemplate; import static oracle.weblogic.kubernetes.utils.FileUtils.replaceStringInFile; import static oracle.weblogic.kubernetes.utils.ImageUtils.createBaseRepoSecret; @@ -126,8 +128,19 @@ public static void initAll(@Namespaces(2) List namespaces) { // Label the domain/operator namespace with istio-injection=enabled Map labelMap = new HashMap<>(); labelMap.put("istio-injection", "enabled"); - assertDoesNotThrow(() -> addLabelsToNamespace(domainNamespace,labelMap)); - assertDoesNotThrow(() -> addLabelsToNamespace(opNamespace,labelMap)); + labelMap.put("istio-discovery", "enabled"); + testUntil( + withStandardRetryPolicy, + addLabelsToNamespace(domainNamespace, labelMap, true), + logger, + "adding istio labels to domain namespace {0}", + domainNamespace); + testUntil( + withStandardRetryPolicy, + addLabelsToNamespace(opNamespace, labelMap, true), + logger, + "adding istio labels to operator namespace {0}", + opNamespace); // install and verify operator installAndVerifyOperator(opNamespace, domainNamespace); @@ -221,33 +234,59 @@ void testIstioModelInImageDomain() throws UnknownHostException, IOException, Int Path targetHttpFile = assertDoesNotThrow( () -> generateFileFromTemplate(srcHttpFile.toString(), "istio-http.yaml", templateMap)); logger.info("Generated Http VS/Gateway file path is {0}", targetHttpFile); + if (OKD) { + replaceStringInFile(targetHttpFile.toString(), domainNamespace + ".org", "*"); + } boolean deployRes = assertDoesNotThrow( () -> deployHttpIstioGatewayAndVirtualservice(targetHttpFile)); assertTrue(deployRes, "Failed to deploy Http Istio Gateway/VirtualService"); - Path srcDrFile = Paths.get(RESOURCE_DIR, "istio", "istio-dr-template.yaml"); - Path targetDrFile = assertDoesNotThrow( - () -> generateFileFromTemplate(srcDrFile.toString(), "istio-dr.yaml", templateMap)); - logger.info("Generated DestinationRule file path is {0}", targetDrFile); - - deployRes = assertDoesNotThrow(() -> deployIstioDestinationRule(targetDrFile)); - assertTrue(deployRes, "Failed to deploy Istio DestinationRule"); - - int istioIngressPort = getIstioHttpIngressPort(); - String host = formatIPv6Host(K8S_NODEPORT_HOST); - logger.info("Istio Ingress Port is {0}", istioIngressPort); - logger.info("host {0}", host); - - // In internal OKE env, use Istio EXTERNAL-IP; in non-OKE env, use K8S_NODEPORT_HOST + ":" + istioIngressPort - String hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null - ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) : host + ":" + istioIngressPort; + Path targetDrFile; + if (!OKD) { + Path srcDrFile = Paths.get(RESOURCE_DIR, "istio", "istio-dr-template.yaml"); + targetDrFile = assertDoesNotThrow( + () -> generateFileFromTemplate(srcDrFile.toString(), "istio-dr.yaml", templateMap)); + logger.info("Generated DestinationRule file path is {0}", targetDrFile); + + deployRes = assertDoesNotThrow(() -> deployIstioDestinationRule(targetDrFile)); + assertTrue(deployRes, "Failed to deploy Istio DestinationRule"); + } else { + Path srcDrFile = Paths.get(RESOURCE_DIR, "istio", "openshift-istio-roles-template.yaml"); + targetDrFile = assertDoesNotThrow( + () -> generateFileFromTemplate(srcDrFile.toString(), "openshift-istio-roles.yaml", templateMap)); + logger.info("Generated Gateway roles and service file path is {0}", targetDrFile); + + deployRes = assertDoesNotThrow(() -> deployIstioDestinationRule(targetDrFile)); + //assertTrue(deployRes, "Failed to deploy Istio DestinationRule"); + + String command = "oc expose service istio-ingressgateway -n " + domainNamespace; + result = exec(command, true); + assertEquals(0, result.exitValue(), "Failed to expose istio-ingressgateway service"); + } + String hostAndPort = ""; String workManagers = "/management/weblogic/latest/domainConfig/selfTuning/workManagers/"; String newWM = workManagers + "newWM/"; - if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !OCNE) { - istioIngressPort = ISTIO_HTTP_HOSTPORT; - hostAndPort = InetAddress.getLocalHost().getHostAddress() + ":" + istioIngressPort; + + if (!OKD) { + int istioIngressPort = getIstioHttpIngressPort(); + String host = formatIPv6Host(K8S_NODEPORT_HOST); + logger.info("Istio Ingress Port is {0}", istioIngressPort); + logger.info("host {0}", host); + + // In internal OKE env, use Istio EXTERNAL-IP; in non-OKE env, use K8S_NODEPORT_HOST + ":" + istioIngressPort + hostAndPort = getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) != null + ? getServiceExtIPAddrtOke(istioIngressServiceName, istioNamespace) : host + ":" + istioIngressPort; + + if (!TestConstants.WLSIMG_BUILDER.equals(TestConstants.WLSIMG_BUILDER_DEFAULT) && !OCNE) { + istioIngressPort = ISTIO_HTTP_HOSTPORT; + hostAndPort = InetAddress.getLocalHost().getHostAddress() + ":" + istioIngressPort; + } + } else { + result = exec("oc get route istio-ingressgateway -n " + domainNamespace + " -o jsonpath='{.spec.host}'", true); + assertEquals(0, result.exitValue(), "Failed to get route"); + hostAndPort = result.stdout(); } String url = "http://" + hostAndPort + "/management/tenant-monitoring/servers/"; @@ -351,7 +390,15 @@ private static void enableStrictMode(String namespace) { private void checkApp(String url) { testUntil( - () -> checkAppUsingHostHeader(url, domainNamespace + ".org"), + () -> { + if (!OKD) { + checkAppUsingHostHeader(url, domainNamespace + ".org"); + return true; + } else { + checkAppUsingHostHeader(url, null); + return true; + } + }, logger, "application to be ready {0}", url); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java index 6c40c9f961a..7e9f276804c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/TestActions.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2024, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.actions; @@ -750,6 +750,20 @@ public static void addLabelsToNamespace(String name, Map labels) Namespace.addLabelsToNamespace(name, labels); } + /** + * Add labels to a namespace. + * + * @param name name of the namespace + * @param labels map of labels to add to the namespace + * @param result to return result + * @return if replaced + */ + public static Callable addLabelsToNamespace(String name, Map labels, boolean result) { + return (() -> { + return Namespace.addLabelsToNamespace(name, labels, result); + }); + } + /** * Create a namespace with unique name. * diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Namespace.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Namespace.java index 0e3317a63e7..8284e7aaf1c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Namespace.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/Namespace.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes.actions.impl; @@ -94,4 +94,33 @@ public static void addLabelsToNamespace(String name, Map labels) getLogger().severe("Namespace {0} not found or failed to add labels", name); } } + + /** + * Add labels to a namespace. + * + * @param name name of the namespace + * @param labels map of labels to add to the namespace + * @param result to return the result + * @return if replaced + * @throws ApiException when adding labels to namespace fails + */ + public static boolean addLabelsToNamespace(String name, Map labels, boolean result) { + boolean success = false; + try { + V1NamespaceList namespaces = Kubernetes.listNamespacesAsObjects(); + if (!namespaces.getItems().isEmpty()) { + for (var ns : namespaces.getItems()) { + if (name.equals(ns.getMetadata().getName())) { + ns.metadata(ns.getMetadata().labels(labels)); + Kubernetes.replaceNamespace(ns); + return true; + } + } + getLogger().severe("Namespace {0} not found or failed to add labels", name); + } + } catch (ApiException ex) { + return success; + } + return success; + } } diff --git a/integration-tests/src/test/resources/istio/openshift-istio-roles-template.yaml b/integration-tests/src/test/resources/istio/openshift-istio-roles-template.yaml new file mode 100644 index 00000000000..c2bec3303f6 --- /dev/null +++ b/integration-tests/src/test/resources/istio/openshift-istio-roles-template.yaml @@ -0,0 +1,83 @@ +# Copyright (c) 2025, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +apiVersion: v1 +kind: Service +metadata: + name: istio-ingressgateway + namespace: NAMESPACE +spec: + type: ClusterIP + selector: + istio: ingressgateway + ports: + - name: http2 + port: 80 + targetPort: 8080 + - name: https + port: 443 + targetPort: 8443 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: istio-ingressgateway + namespace: NAMESPACE +spec: + selector: + matchLabels: + istio: ingressgateway + template: + metadata: + annotations: + # Select the gateway injection template (rather than the default sidecar template) + inject.istio.io/templates: gateway + labels: + # Set a unique label for the gateway. This is required to ensure Gateways can select this workload + istio: ingressgateway + # Enable gateway injection. If connecting to a revisioned control plane, replace with "istio.io/rev: revision-name" + sidecar.istio.io/inject: "true" + spec: + containers: + - name: istio-proxy + image: auto # The image will automatically update each time the pod starts. + +--- +# Set up roles to allow reading credentials for TLS +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: istio-ingressgateway-sds + namespace: NAMESPACE +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: istio-ingressgateway-sds +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: istio-ingressgateway-sds +subjects: +- kind: ServiceAccount + name: default +#--- +# Allow outside traffic to access the gateway +# This is optional, only needed in case your cluster contains restrictive NetworkPolicies +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: gatewayingress +#spec: +# podSelector: +# matchLabels: +# istio: ingressgateway +# ingress: +# - {} +# policyTypes: +# - Ingress + From 2fb9ebc75cd0125b687da50fb8d7d1f440bcd60b Mon Sep 17 00:00:00 2001 From: jshum Date: Thu, 8 May 2025 07:50:15 -0500 Subject: [PATCH 330/356] Fix domainstatus observedGeneration no updated in successful MII online update --- .../oracle/kubernetes/operator/DomainStatusUpdater.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java b/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java index 97c93e84aa5..6ae5717ce87 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainStatusUpdater.java @@ -87,6 +87,7 @@ import static oracle.kubernetes.operator.ProcessingConstants.DOMAIN_TOPOLOGY; import static oracle.kubernetes.operator.ProcessingConstants.MII_DYNAMIC_UPDATE; import static oracle.kubernetes.operator.ProcessingConstants.MII_DYNAMIC_UPDATE_RESTART_REQUIRED; +import static oracle.kubernetes.operator.ProcessingConstants.MII_DYNAMIC_UPDATE_SUCCESS; import static oracle.kubernetes.operator.ProcessingConstants.SERVER_HEALTH_MAP; import static oracle.kubernetes.operator.ProcessingConstants.SERVER_STATE_MAP; import static oracle.kubernetes.operator.WebLogicConstants.RUNNING_STATE; @@ -429,10 +430,12 @@ static class DomainStatusUpdaterContext { private DomainStatus newStatus; private final List newEvents = new ArrayList<>(); final boolean endOfProcessing; + private Packet packet; DomainStatusUpdaterContext(Packet packet, DomainStatusUpdaterStep domainStatusUpdaterStep) { info = DomainPresenceInfo.fromPacket(packet).orElseThrow(); isMakeRight = MakeRightDomainOperation.isMakeRight(packet); + this.packet = packet; this.domainStatusUpdaterStep = domainStatusUpdaterStep; endOfProcessing = (Boolean) packet.getOrDefault(ProcessingConstants.END_OF_PROCESSING, Boolean.FALSE); } @@ -546,7 +549,10 @@ Step createUpdateSteps(Step next) { if (!isStatusUnchanged()) { result.add(createDomainStatusReplaceStep()); } else { - if (endOfProcessing && isMakeRight) { + boolean successFullDynamicUpdate = MII_DYNAMIC_UPDATE_SUCCESS.equals(packet.get(MII_DYNAMIC_UPDATE)) + || MII_DYNAMIC_UPDATE_RESTART_REQUIRED.equals(packet.get(MII_DYNAMIC_UPDATE)); + + if ((endOfProcessing && isMakeRight) || successFullDynamicUpdate) { Optional.ofNullable(createDomainStatusObservedGenerationReplaceStep()).ifPresent(result::add); } } From 35ddb0f49823a9e638596beb74522199544125f7 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Thu, 8 May 2025 18:30:17 +0000 Subject: [PATCH 331/356] Merge branch 'jackson-dependencies' into 'main' Dependency updates See merge request weblogic-cloud/weblogic-kubernetes-operator!4961 (cherry picked from commit b49489497e424bd1799e138ab07a68d9a838c36d) 03e17051 Dependency updates --- operator/pom.xml | 10 ++++++++++ pom.xml | 28 ++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/operator/pom.xml b/operator/pom.xml index 7cc2fa2542d..5beeea3cad3 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -405,6 +405,16 @@ jackson-dataformat-yaml + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-annotations + + com.fasterxml.jackson.core jackson-databind diff --git a/pom.xml b/pom.xml index a1190f41b7e..eb65c8f48d0 100644 --- a/pom.xml +++ b/pom.xml @@ -565,6 +565,26 @@ jackson-dataformat-yaml ${jackson-version} + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + ${jackson-version} + + + com.fasterxml.jackson.module + jackson-module-jakarta-xmlbind-annotations + ${jackson-version} + com.fasterxml.jackson.core jackson-databind @@ -688,7 +708,7 @@ 3.8.1 3.6.0 3.5.0 - 10.23.0 + 10.23.1 1.0 3.6.0 3.2.7 @@ -726,10 +746,10 @@ 4.0.2 6.1.0 0.16.0 - 2.18.3 - 2.18.3 + 2.19.0 + 2.19.0 2.4 - 2.13.0 + 2.13.1 12.1.1 2.0.17 1.5.18 From bbdaa8d4af141edfbb852b83d35eba366a0f51e7 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 12 May 2025 14:41:00 +0000 Subject: [PATCH 332/356] Merge branch 'owls-125094' into 'main' Create default startup probe See merge request weblogic-cloud/weblogic-kubernetes-operator!4963 (cherry picked from commit bbd1a94242970b41e864cfaedebac7078b50b84a) d92e8e17 Work in progress af9071ab Create default startup probe --- .../operator/helpers/PodStepContext.java | 89 +++++++++++++++---- .../kubernetes/operator/tuning/PodTuning.java | 12 ++- .../operator/tuning/TuningParameters.java | 32 ++++++- .../operator/helpers/PodHelperTestBase.java | 7 ++ 4 files changed, 119 insertions(+), 21 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java index 7b0daae15df..688eb922f5a 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/PodStepContext.java @@ -749,11 +749,12 @@ private List getVolumes(String domainUid) { @Override protected V1Container createPrimaryContainer() { final PodTuning podTuning = TuningParameters.getInstance().getPodTuning(); + Pair probes = createLivenessAndStartupProbe(podTuning); V1Container v1Container = super.createPrimaryContainer() .ports(getContainerPorts()) .lifecycle(createLifecycle()) - .livenessProbe(createLivenessProbe(podTuning)) - .startupProbe(getStartupProbe()); + .livenessProbe(probes.left()) + .startupProbe(probes.right()); if (!mockWls()) { v1Container.readinessProbe(createReadinessProbe(podTuning)); @@ -968,6 +969,11 @@ private V1Probe getReadinessProbe() { .map(V1ProbeBuilder::new).map(V1ProbeBuilder::build).orElse(new V1Probe()); } + private Pair createLivenessAndStartupProbe(PodTuning tuning) { + V1Probe livenessProbe = createLivenessProbe(tuning); + return new Pair<>(livenessProbe, createStartupProbe(livenessProbe, tuning)); + } + private V1Probe createLivenessProbe(PodTuning tuning) { V1Probe livenessProbe = getLivenessProbe(); @@ -988,21 +994,12 @@ private V1Probe createLivenessProbe(PodTuning tuning) { livenessProbe.setSuccessThreshold(tuning.getLivenessProbeSuccessThreshold()); } - try { - V1HTTPGetAction httpGetAction = livenessProbe.getHttpGet(); - if (httpGetAction != null) { - if (httpGetAction.getPort() == null) { - httpGetAction.setPort(new IntOrString(getLocalAdminProtocolChannelPort())); - } - if (httpGetAction.getScheme() == null && isLocalAdminProtocolChannelSecure()) { - httpGetAction.setScheme("HTTPS"); - } - } else if (livenessProbe.getExec() == null - && livenessProbe.getTcpSocket() == null && livenessProbe.getGrpc() == null) { - livenessProbe.setExec(execAction(LIVENESS_PROBE)); - } - } catch (Exception e) { - // do nothing + V1HTTPGetAction httpGetAction = livenessProbe.getHttpGet(); + if (httpGetAction != null) { + initializeHttpGetAction(httpGetAction); + } else if (livenessProbe.getExec() == null + && livenessProbe.getTcpSocket() == null && livenessProbe.getGrpc() == null) { + livenessProbe.setExec(execAction(LIVENESS_PROBE)); } return livenessProbe; @@ -1013,8 +1010,52 @@ private V1Probe getLivenessProbe() { .map(V1ProbeBuilder::new).map(V1ProbeBuilder::build).orElse(new V1Probe()); } + private V1Probe createStartupProbe(V1Probe livenessProbe, PodTuning tuning) { + V1Probe startupProbe = getStartupProbe(); + + if (startupProbe.getInitialDelaySeconds() == null && tuning.getStartupProbeInitialDelaySeconds() > 0) { + startupProbe.setInitialDelaySeconds(tuning.getStartupProbeInitialDelaySeconds()); + } + if (startupProbe.getTimeoutSeconds() == null) { + startupProbe.setTimeoutSeconds(tuning.getStartupProbeTimeoutSeconds()); + } + if (startupProbe.getPeriodSeconds() == null) { + startupProbe.setPeriodSeconds(tuning.getStartupProbePeriodSeconds()); + } + if (startupProbe.getFailureThreshold() == null) { + startupProbe.setFailureThreshold(tuning.getStartupProbeFailureThreshold()); + } + if (startupProbe.getSuccessThreshold() == null + && tuning.getStartupProbeSuccessThreshold() != DEFAULT_SUCCESS_THRESHOLD) { + startupProbe.setSuccessThreshold(tuning.getStartupProbeSuccessThreshold()); + } + + V1HTTPGetAction httpGetAction = startupProbe.getHttpGet(); + if (httpGetAction != null) { + initializeHttpGetAction(httpGetAction); + } else if (startupProbe.getExec() == null + && startupProbe.getTcpSocket() == null && startupProbe.getGrpc() == null) { + startupProbe.setHttpGet(livenessProbe.getHttpGet()); + startupProbe.setExec(livenessProbe.getExec()); + startupProbe.setTcpSocket(livenessProbe.getTcpSocket()); + startupProbe.setGrpc(livenessProbe.getGrpc()); + } + + return startupProbe; + } + private V1Probe getStartupProbe() { - return getServerSpec().getStartupProbe(); + return Optional.ofNullable(getServerSpec().getStartupProbe()) + .map(V1ProbeBuilder::new).map(V1ProbeBuilder::build).orElse(new V1Probe()); + } + + private void initializeHttpGetAction(@Nonnull V1HTTPGetAction httpGetAction) { + if (httpGetAction.getPort() == null) { + httpGetAction.setPort(new IntOrString(getLocalAdminProtocolChannelPort())); + } + if (httpGetAction.getScheme() == null && isLocalAdminProtocolChannelSecure()) { + httpGetAction.setScheme("HTTPS"); + } } private boolean mockWls() { @@ -1381,6 +1422,15 @@ private void restoreSecurityContextEmptyInitContainer(V1Pod recipe, V1Pod curren })); } + private void restoreNoStartupProbe(V1Pod recipe, V1Pod currentPod) { + Optional.ofNullable(recipe.getSpec().getContainers()) + .ifPresent(containers -> + containers.forEach(container -> Optional.ofNullable(currentPod.getSpec().getContainers()) + .flatMap(currentContainers -> currentContainers.stream() + .filter(cc -> cc.getName().equals(container.getName())).findFirst()) + .ifPresent(match -> container.setStartupProbe(match.getStartupProbe())))); + } + private boolean canAdjustRecentOperatorMajorVersion3HashToMatch(V1Pod currentPod, String requiredHash) { // start with list of adjustment methods // generate stream of combinations @@ -1396,7 +1446,8 @@ private boolean canAdjustRecentOperatorMajorVersion3HashToMatch(V1Pod currentPod Pair.of("restoreFluentdVolume", this::restoreFluentdVolume), Pair.of("restoreSecurityContext", this::restoreSecurityContext), Pair.of("restoreSecurityContextEmpty", this::restoreSecurityContextEmpty), - Pair.of("restoreSecurityContextEmptyInitContainer", this::restoreSecurityContextEmptyInitContainer)); + Pair.of("restoreSecurityContextEmptyInitContainer", this::restoreSecurityContextEmptyInitContainer), + Pair.of("restoreNoStartupProbe", this::restoreNoStartupProbe)); return Combinations.of(adjustments) .map(adjustment -> adjustedHash(currentPod, adjustment)) .anyMatch(requiredHash::equals); diff --git a/operator/src/main/java/oracle/kubernetes/operator/tuning/PodTuning.java b/operator/src/main/java/oracle/kubernetes/operator/tuning/PodTuning.java index 2b12047fad0..61e674a9e0b 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/tuning/PodTuning.java +++ b/operator/src/main/java/oracle/kubernetes/operator/tuning/PodTuning.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, Oracle and/or its affiliates. +// Copyright (c) 2022, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.tuning; @@ -27,4 +27,14 @@ public interface PodTuning { int getLivenessProbeSuccessThreshold(); int getLivenessProbeFailureThreshold(); + + int getStartupProbeInitialDelaySeconds(); + + int getStartupProbeTimeoutSeconds(); + + int getStartupProbePeriodSeconds(); + + int getStartupProbeSuccessThreshold(); + + int getStartupProbeFailureThreshold(); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/tuning/TuningParameters.java b/operator/src/main/java/oracle/kubernetes/operator/tuning/TuningParameters.java index eb92f5d9620..87f08215942 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/tuning/TuningParameters.java +++ b/operator/src/main/java/oracle/kubernetes/operator/tuning/TuningParameters.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.tuning; @@ -56,6 +56,11 @@ public class TuningParameters { public static final String LIVENESS_PERIOD_SECONDS = "livenessProbePeriodSeconds"; public static final String LIVENESS_SUCCESS_COUNT_THRESHOLD = "livenessProbeSuccessThreshold"; public static final String LIVENESS_FAILURE_COUNT_THRESHOLD = "livenessProbeFailureThreshold"; + public static final String STARTUP_INITIAL_DELAY_SECONDS = "startupProbeInitialDelaySeconds"; + public static final String STARTUP_TIMEOUT_SECONDS = "startupProbeTimeoutSeconds"; + public static final String STARTUP_PERIOD_SECONDS = "startupProbePeriodSeconds"; + public static final String STARTUP_SUCCESS_COUNT_THRESHOLD = "startupProbeSuccessThreshold"; + public static final String STARTUP_FAILURE_COUNT_THRESHOLD = "startupProbeFailureThreshold"; public static final String INITIALIZATION_RETRY_DELAY_SECONDS = "initializationRetryDelaySeconds"; public static final String UNCHANGED_COUNT_TO_DELAY_STATUS_RECHECK = "statusUpdateUnchangedCountToDelayStatusRecheck"; @@ -424,6 +429,31 @@ public int getLivenessProbeSuccessThreshold() { public int getLivenessProbeFailureThreshold() { return getParameter(LIVENESS_FAILURE_COUNT_THRESHOLD, 1); } + + @Override + public int getStartupProbeInitialDelaySeconds() { + return getParameter(STARTUP_INITIAL_DELAY_SECONDS, 0); + } + + @Override + public int getStartupProbeTimeoutSeconds() { + return getParameter(STARTUP_TIMEOUT_SECONDS, 5); + } + + @Override + public int getStartupProbePeriodSeconds() { + return getParameter(STARTUP_PERIOD_SECONDS, 5); + } + + @Override + public int getStartupProbeSuccessThreshold() { + return getParameter(STARTUP_SUCCESS_COUNT_THRESHOLD, 1); + } + + @Override + public int getStartupProbeFailureThreshold() { + return getParameter(STARTUP_FAILURE_COUNT_THRESHOLD, 20); + } } private class FeatureGatesImpl implements FeatureGates { diff --git a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java index 5e7f28d326d..68cf9cd88dc 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java +++ b/operator/src/test/java/oracle/kubernetes/operator/helpers/PodHelperTestBase.java @@ -1550,6 +1550,13 @@ void whenPodCreated_livenessProbeHasLivenessCommand() { contains("/weblogic-operator/scripts/livenessProbe.sh")); } + @Test + void whenPodCreated_startupProbeHasLivenessCommand() { + assertThat( + getCreatedPodSpecContainer().getStartupProbe().getExec().getCommand(), + contains("/weblogic-operator/scripts/livenessProbe.sh")); + } + @Test void whenPodCreated_livenessProbeHasDefinedTuning() { assertThat( From 8a81c8772c23bb5b39623d94ce39d5b014c2e802 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Mon, 12 May 2025 10:47:45 -0400 Subject: [PATCH 333/356] Dependency updates --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb65c8f48d0..aacebcb4102 100644 --- a/pom.xml +++ b/pom.xml @@ -755,7 +755,7 @@ 1.5.18 4.30.2 2.5.2 - 10.2 + 10.3 ${project.basedir}/src-generated-swagger ${root-generated-swagger}/main/java ${project.basedir}/swagger/domain.json From 2e270a906c52f6b9658cc529085f47015cee9b52 Mon Sep 17 00:00:00 2001 From: Sankar Periyathambi neelakandan Date: Tue, 13 May 2025 17:37:03 +0000 Subject: [PATCH 334/356] Fix for FMW sample DB creation failure in nightly runs --- .../kubernetes/ItFmwDomainOnPVSample.java | 44 +++++++--- .../weblogic/kubernetes/TestConstants.java | 3 + .../common/oracle.db.19plus.yaml | 81 +++++++++++++++++++ .../common/oracle.db.yaml | 3 + .../start-db-service.sh | 49 +++++++++-- .../dpv-sample-wrapper/create-secrets.sh | 8 +- .../dpv-sample-wrapper/env-init.sh | 3 +- .../domain-on-pv/run-test.sh | 9 ++- .../domain-on-pv/test-env.sh | 5 +- .../domain-on-pv/util-misc.sh | 4 +- 10 files changed, 177 insertions(+), 32 deletions(-) create mode 100644 kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.19plus.yaml diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java index 54f0722d81f..9b532b999c3 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItFmwDomainOnPVSample.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, Oracle and/or its affiliates. +// Copyright (c) 2023, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.weblogic.kubernetes; @@ -31,6 +31,7 @@ import static oracle.weblogic.kubernetes.TestConstants.BUSYBOX_TAG; import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.DB_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.DB_PDB_ID_DEFAULT_19C; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TAG; import static oracle.weblogic.kubernetes.TestConstants.FMWINFRA_IMAGE_TO_USE_IN_SPEC; import static oracle.weblogic.kubernetes.TestConstants.IMAGE_NAME_OPERATOR; @@ -63,6 +64,7 @@ import static oracle.weblogic.kubernetes.utils.SampleUtils.createPVHostPathAndChangePermissionInKindCluster; import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test and verify Domain on PV FMW domain sample. @@ -141,6 +143,7 @@ public static void initAll(@Namespaces(4) List namespaces) { envMap.put("OPER_IMAGE_NAME", TEST_IMAGES_PREFIX + IMAGE_NAME_OPERATOR); envMap.put("DOMAIN_CREATION_IMAGE_NAME", TEST_IMAGES_PREFIX + DOMAIN_CREATION_IMAGE_NAME); envMap.put("DB_IMAGE_PULL_SECRET", BASE_IMAGES_REPO_SECRET_NAME); + envMap.put("DB_PDB_ID", DB_PDB_ID_DEFAULT_19C); // kind cluster uses openjdk which is not supported by image tool if (WIT_JAVA_HOME != null) { @@ -181,7 +184,7 @@ public static void initAll(@Namespaces(4) List namespaces) { @Order(1) public void testInstallOperator() { String backupReports = backupReports(UniqueName.uniqueName(this.getClass().getSimpleName())); - execTestScriptAndAssertSuccess("-oper", "Failed to run -oper"); + assertTrue(execTestScriptAndAssertSuccess("-oper", "Failed to run -oper")); restoreReports(backupReports); } @@ -191,7 +194,8 @@ public void testInstallOperator() { @Test @Order(2) public void testInstallTraefik() { - execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik"); + Assumptions.assumeTrue(previousTestSuccessful); + assertTrue(execTestScriptAndAssertSuccess("-traefik", "Failed to run -traefik")); } /** @@ -200,7 +204,8 @@ public void testInstallTraefik() { @Test @Order(3) public void testPrecleandb() { - execTestScriptAndAssertSuccess("-precleandb", "Failed to run -precleandb"); + Assumptions.assumeTrue(previousTestSuccessful); + assertTrue(execTestScriptAndAssertSuccess("-precleandb", "Failed to run -precleandb")); } /** @@ -209,25 +214,38 @@ public void testPrecleandb() { @Test @Order(4) public void testCreatedb() { + Assumptions.assumeTrue(previousTestSuccessful); logger.info("test case for creating a db"); if (KIND_REPO != null) { String dbimage = DB_IMAGE_NAME + ":" + DB_IMAGE_TAG; logger.info("loading image {0} to kind", dbimage); imagePush(dbimage); } - execTestScriptAndAssertSuccess("-db", "Failed to run -db"); + assertTrue(execTestScriptAndAssertSuccess("-db", "Failed to run -db")); } /** - * Test Domain on PV sample building image for FMW domain use case. + * Test Domain on PV sample - Initialize schemas in the DB. */ @Test @Order(5) + public void testCreateRCU() { + Assumptions.assumeTrue(previousTestSuccessful); + logger.info("test case for initializing schemas in the DB"); + assertTrue(execTestScriptAndAssertSuccess("-rcu", "Failed to run -rcu")); + } + + /** + * Test Domain on PV sample building image for FMW domain use case. + */ + @Test + @Order(6) public void testInitialImage() { + Assumptions.assumeTrue(previousTestSuccessful); logger.info("test case for building image"); imagePull(BUSYBOX_IMAGE + ":" + BUSYBOX_TAG); imageTag(BUSYBOX_IMAGE + ":" + BUSYBOX_TAG, "busybox"); - execTestScriptAndAssertSuccess("-initial-image", "Failed to run -initial-image"); + assertTrue(execTestScriptAndAssertSuccess("-initial-image", "Failed to run -initial-image")); ExecResult result = Command.withParams( new CommandParams() .command(WLSIMG_BUILDER + " images") @@ -247,8 +265,9 @@ public void testInitialImage() { * Test Domain on PV sample create FMW domain use case. */ @Test - @Order(6) + @Order(7) public void testInitialMain() { + Assumptions.assumeTrue(previousTestSuccessful); logger.info("test case for creating a FMW domain"); // load the base image to kind if using kind cluster @@ -262,7 +281,7 @@ public void testInitialMain() { withLongRetryPolicy, checkTestScriptAndAssertSuccess("-initial-main", "Failed to run -initial-main"), logger, - "create PV HostPath and change Permission in Kind Cluster"); + "creating FMW domain"); } /** @@ -273,7 +292,6 @@ public void testInitialMain() { private boolean execTestScriptAndAssertSuccess(String arg, String errString) { - Assumptions.assumeTrue(previousTestSuccessful); previousTestSuccessful = false; String command = domainOnPvSampleScript @@ -293,14 +311,14 @@ private boolean execTestScriptAndAssertSuccess(String arg, && result.stdout() != null && result.stdout().contains("Finished without errors"); - String outStr = errString; - outStr += ", command=\n{\n" + command + "\n}\n"; + String outStr = success ? "Running test script succeeds: " : errString + ":"; + outStr += " command=\n{\n" + command + "\n}\n"; outStr += ", stderr=\n{\n" + (result != null ? result.stderr() : "") + "\n}\n"; outStr += ", stdout=\n{\n" + (result != null ? result.stdout() : "") + "\n}\n"; logger.info("output String is: {0}", outStr); - previousTestSuccessful = true; + previousTestSuccessful = success; return success; } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index 74c7d40a5ab..b233ebb90fd 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -114,6 +114,9 @@ public interface TestConstants { public static final String DB_IMAGE_NAME_DEFAULT = "test-images/database/enterprise"; public static final String DB_PREBUILT_IMAGE_NAME_DEFAULT = "test-images/database/express"; public static final String DB_IMAGE_TAG_DEFAULT = "19.3.0.0"; + public static final String DB_IMAGE_TAG_DEFAULT_12C = "12.2.0.1-slim"; + public static final String DB_PDB_ID_DEFAULT_19C = "orclpdb1"; + public static final String DB_PDB_ID_DEFAULT_12C = "devpdb.k8s"; // repository to push the domain images created during test execution // (a) for kind cluster push to kind repo diff --git a/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.19plus.yaml b/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.19plus.yaml new file mode 100644 index 00000000000..937cc138363 --- /dev/null +++ b/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.19plus.yaml @@ -0,0 +1,81 @@ +# Copyright (c) 2025, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +apiVersion: v1 +kind: Service +metadata: + name: oracle-db + namespace: default +spec: + ports: + - name: tns + port: 1521 + protocol: TCP + targetPort: 1521 + nodePort: 30011 + selector: + app.kubernetes.io/instance: dev + app.kubernetes.io/name: oracle-db + sessionAffinity: None + type: LoadBalancer +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: oracle-db + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: dev + app.kubernetes.io/name: oracle-db + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/instance: dev + app.kubernetes.io/name: oracle-db + spec: + containers: + - env: + - name: ORACLE_SID + value: ORCLCDB + - name: ORACLE_PDB + value: ORCLPDB1 + - name: DB_DOMAIN + value: k8s + - name: DB_BUNDLE + value: basic + - name: ORACLE_PWD + valueFrom: + secretKeyRef: + name: oracle-db-secret + key: password + image: container-registry.oracle.com/database/enterprise:19.3.0.0 + imagePullPolicy: IfNotPresent + name: oracle-db + ports: + - containerPort: 1521 + name: tns + protocol: TCP + resources: + requests: + cpu: "2" + memory: "24Gi" + limits: + cpu: "4" + memory: "40Gi" + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + imagePullSecrets: + - name: docker-store diff --git a/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml b/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml index badba008c6e..b1a677d2d6a 100644 --- a/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml +++ b/kubernetes/samples/scripts/create-oracle-db-service/common/oracle.db.yaml @@ -1,3 +1,6 @@ +# Copyright (c) 2020, 2025, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + apiVersion: v1 kind: Service metadata: diff --git a/kubernetes/samples/scripts/create-oracle-db-service/start-db-service.sh b/kubernetes/samples/scripts/create-oracle-db-service/start-db-service.sh index 1241d2aa616..95d3486f327 100755 --- a/kubernetes/samples/scripts/create-oracle-db-service/start-db-service.sh +++ b/kubernetes/samples/scripts/create-oracle-db-service/start-db-service.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2019, 2022, Oracle and/or its affiliates. +# Copyright (c) 2019, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Bring up Oracle DB Instance in [default] NameSpace with a NodePort Service @@ -9,7 +9,7 @@ scriptDir="$( cd "$( dirname "${script}" )" && pwd )" source ${scriptDir}/../common/utility.sh usage() { - echo "usage: ${script} [-a ] [-p ] [-i ] [-s ] [-n ] [-h]" + echo "usage: ${script} [-a ] [-p ] [-i ] [-s ] [-n ] [-l ] [-h]" echo " -a DB Sys Account Password Secret Name (optional)" echo " (default: oracle-db-secret, secret must include a key named 'password')" echo " If this secret is not deployed, then the database will not successfully deploy." @@ -22,6 +22,7 @@ usage() { echo " If this secret is not deployed, then Kubernetes will try pull anonymously." echo " -n Configurable Kubernetes NameSpace for Oracle DB Service (optional)" echo " (default: default) " + echo " -l db pdb id for Oracle DB service (optional, default: devpdb.k8s)" echo " -h Help" exit $1 } @@ -31,8 +32,9 @@ nodeport=30011 dbimage="container-registry.oracle.com/database/enterprise:12.2.0.1-slim" pullsecret="docker-store" namespace="default" +pdbid="devpdb.k8s" -while getopts ":a:p:i:s:n:h:" opt; do +while getopts ":a:p:i:s:n:h:l:" opt; do case $opt in a) syssecret="${OPTARG}" ;; @@ -44,6 +46,8 @@ while getopts ":a:p:i:s:n:h:" opt; do ;; n) namespace="${OPTARG}" ;; + l) pdbid="${OPTARG}" + ;; h) usage 0 ;; *) usage 1 @@ -65,11 +69,20 @@ echo "NodePort[$nodeport] ImagePullSecret[$pullsecret] Image[${dbimage}] NameSpa #create unique db yaml file if does not exists dbYaml=${scriptDir}/common/oracle.db.${namespace}.yaml + if [ ! -f "$dbYaml" ]; then echo "$dbYaml does not exist." - cp ${scriptDir}/common/oracle.db.yaml ${dbYaml} + # Choose template based on dbimage version + if echo "$dbimage" | grep -q "12\."; then + templateYaml="${scriptDir}/common/oracle.db.yaml" + else + templateYaml="${scriptDir}/common/oracle.db.19plus.yaml" + fi + echo "Using template: $templateYaml" + cp "$templateYaml" "$dbYaml" fi + # Modify ImagePullSecret and DatabaseImage based on input sed -i -e '$d' ${dbYaml} echo ' - name: docker-store' >> ${dbYaml} @@ -92,6 +105,7 @@ fi ${KUBERNETES_CLI:-kubectl} delete service oracle-db -n ${namespace} --ignore-not-found echo "Applying Kubernetes YAML file '${dbYaml}' to start database." +cat ${dbYaml} ${KUBERNETES_CLI:-kubectl} apply -f ${dbYaml} detectPod ${namespace} @@ -107,9 +121,30 @@ checkService oracle-db ${namespace} ${KUBERNETES_CLI:-kubectl} get po -n ${namespace} ${KUBERNETES_CLI:-kubectl} get service -n ${namespace} -${KUBERNETES_CLI:-kubectl} cp ${scriptDir}/common/checkDbState.sh -n ${namespace} ${dbpod}:/home/oracle/ +logfile="/tmp/setupDB.log" +max=60 +counter=0 +while [ $counter -le ${max} ] +do + ${KUBERNETES_CLI:-kubectl} logs ${dbpod} -n ${namespace} > $logfile + grep -i "DATABASE IS READY" $logfile + [[ $? == 0 ]] && break; + ((counter++)) + echo "${KUBERNETES_CLI:-kubectl} describe pod ${dbpod} -n ${namespace}" + ${KUBERNETES_CLI:-kubectl} describe pod ${dbpod} -n ${namespace} + echo "[$counter/${max}] Retrying for Oracle Database Availability..." + sleep 60 +done + +if [ $counter -gt ${max} ]; then + echo "[ERRORR] Oracle DB Service is not ready after [${max}] iterations ..." + exit -1 +fi + +# for db 19c only +echo " set sys password " +${KUBERNETES_CLI:-kubectl} exec -it ${dbpod} -n ${namespace} -- /bin/bash setPassword.sh Oradoc_db1 -${KUBERNETES_CLI:-kubectl} exec -it ${dbpod} -n ${namespace} -- /bin/bash /home/oracle/checkDbState.sh if [ $? != 0 ]; then echo "######################"; echo "[ERROR] Could not create Oracle DB Service, check the pod log for pod ${dbpod} in namespace ${namespace}"; @@ -122,4 +157,4 @@ if [ ! "${nodeport}" = "none" ]; then else echo "Oracle DB Service is RUNNING and does not specify a public NodePort" fi -echo "Oracle DB Service URL [oracle-db.${namespace}.svc.cluster.local:1521/devpdb.k8s]" +echo "Oracle DB Service URL [oracle-db.${namespace}.svc.cluster.local:1521/${pdbid}]" diff --git a/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/create-secrets.sh b/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/create-secrets.sh index d646f174216..8c7fa1fcdcb 100755 --- a/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/create-secrets.sh +++ b/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/create-secrets.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -15,7 +15,7 @@ # Optional environment variables (see README for details): # # WORKDIR, DOMAIN_UID, DOMAIN_NAMESPACE, WDT_DOMAIN_TYPE, -# DB_NAMESPACE, INCLUDE_DOMAIN_CREATION_CONFIGMAP, CORRECTED_DATASOURCE_SECRET, +# DB_NAMESPACE, DB_PDB_ID, INCLUDE_DOMAIN_CREATION_CONFIGMAP, CORRECTED_DATASOURCE_SECRET, # KUBERNETES_CLI set -eu @@ -51,7 +51,7 @@ if [ "$WDT_DOMAIN_TYPE" = "JRF" ]; then -d $DOMAIN_UID -n $DOMAIN_NAMESPACE \ -l rcu_prefix=FMW${CUSTOM_DOMAIN_NAME} \ -l rcu_schema_password=Oradoc_db1 \ - -l rcu_db_conn_string=oracle-db.${DB_NAMESPACE}.svc.cluster.local:1521/devpdb.k8s + -l rcu_db_conn_string=oracle-db.${DB_NAMESPACE}.svc.cluster.local:1521/${DB_PDB_ID} echo "@@ Info: Creating OPSS wallet password secret (ignored unless domain type is JRF)" $WORKDIR/domain-on-pv/utils/create-secret.sh $DRY_RUN -s ${DOMAIN_UID}-opss-wallet-password-secret \ -d $DOMAIN_UID -n $DOMAIN_NAMESPACE \ @@ -84,5 +84,5 @@ if [ "${INCLUDE_DOMAIN_CREATION_CONFIGMAP}" = "true" ]; then -l "user=sys as sysdba" \ -l password=$dspw \ -l max-capacity=$dscap \ - -l url=jdbc:oracle:thin:@oracle-db.${DB_NAMESPACE}.svc.cluster.local:1521/devpdb.k8s + -l url=jdbc:oracle:thin:@oracle-db.${DB_NAMESPACE}.svc.cluster.local:1521/${DB_PDB_ID} fi diff --git a/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/env-init.sh b/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/env-init.sh index 363b77a5d1a..a3f5ef416e4 100755 --- a/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/env-init.sh +++ b/operator/integration-tests/domain-on-pv/dpv-sample-wrapper/env-init.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # This file sets the defaults for the sample wrapper scripts, @@ -34,6 +34,7 @@ DOMAIN_RESOURCE_TEMPLATE="${DOMAIN_RESOURCE_TEMPLATE:-dpv-domain.yaml.template-$ DOMAIN_RESOURCE_FILENAME="${DOMAIN_RESOURCE_FILENAME:-domain-resources/dpv-${DOMAIN_UID}.yaml}" DB_NAMESPACE=${DB_NAMESPACE:-default} +DB_PDB_ID=${DB_PDB_ID:-devpdb.k8s} DOWNLOAD_WIT=${DOWNLOAD_WIT:-when-missing} DOWNLOAD_WDT=${DOWNLOAD_WDT:-when-missing} diff --git a/operator/integration-tests/domain-on-pv/run-test.sh b/operator/integration-tests/domain-on-pv/run-test.sh index 59c76879530..766d5157708 100755 --- a/operator/integration-tests/domain-on-pv/run-test.sh +++ b/operator/integration-tests/domain-on-pv/run-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2023, Oracle and/or its affiliates. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -58,6 +58,7 @@ usage() { DOMAIN_IMAGE_PULL_SECRET_NAME : (not set) DB_NAMESPACE : default (used by -db and -rcu) DB_IMAGE_PULL_SECRET : repo secret (used by -db and -rcu) + DB_PDB_ID : devpdb.k8s (used by -rcu) TRAEFIK_NAMESPACE : traefik-operator-ns (used by -traefik and by tests) TRAEFIK_HTTP_NODEPORT : 30305 (used by -traefik and by tests, can be 0 to dynamically choose) TRAEFIK_HTTPS_NODEPORT : 30433 (used by -traefik, can be 0 to dynamically choose) @@ -276,9 +277,9 @@ if [ "$DO_DB" = "true" ]; then doCommand "\$DBSAMPLEDIR/stop-db-service.sh -n \$DB_NAMESPACE" if [ ! -z "$DB_IMAGE_PULL_SECRET" ]; then - doCommand "\$DBSAMPLEDIR/start-db-service.sh -n \$DB_NAMESPACE -i \$DB_IMAGE_NAME:\$DB_IMAGE_TAG -p \$DB_NODE_PORT -s \$DB_IMAGE_PULL_SECRET" + doCommand "\$DBSAMPLEDIR/start-db-service.sh -n \$DB_NAMESPACE -i \$DB_IMAGE_NAME:\$DB_IMAGE_TAG -p \$DB_NODE_PORT -l \$DB_PDB_ID -s \$DB_IMAGE_PULL_SECRET" else - doCommand "\$DBSAMPLEDIR/start-db-service.sh -n \$DB_NAMESPACE -i \$DB_IMAGE_NAME:\$DB_IMAGE_TAG -p \$DB_NODE_PORT" + doCommand "\$DBSAMPLEDIR/start-db-service.sh -n \$DB_NAMESPACE -i \$DB_IMAGE_NAME:\$DB_IMAGE_TAG -p \$DB_NODE_PORT -l \$DB_PDB_ID" fi fi @@ -307,7 +308,7 @@ if [ "$DO_RCU" = "true" ]; then doCommand -c "cd \$SRCDIR/kubernetes/samples/scripts/create-rcu-schema" rcuCommand="./create-rcu-schema.sh" - rcuCommand+=" -d oracle-db.\$DB_NAMESPACE.svc.cluster.local:1521/devpdb.k8s" # DB url + rcuCommand+=" -d oracle-db.\$DB_NAMESPACE.svc.cluster.local:1521/\$DB_PDB_ID" # DB url rcuCommand+=" -s FMW$_custom_domain_name_" # RCU schema prefix if [ ! -z "$DB_IMAGE_PULL_SECRET" ]; then rcuCommand+=" -p \$DB_IMAGE_PULL_SECRET" # FMW infra image pull secret for rcu pod diff --git a/operator/integration-tests/domain-on-pv/test-env.sh b/operator/integration-tests/domain-on-pv/test-env.sh index 4d1a42592fd..2201a68974c 100755 --- a/operator/integration-tests/domain-on-pv/test-env.sh +++ b/operator/integration-tests/domain-on-pv/test-env.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2023, 2024, Oracle and/or its affiliates. +# Copyright (c) 2023, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -33,6 +33,7 @@ export DB_NODE_PORT=${DB_NODE_PORT:-30011} export DB_IMAGE_NAME=${DB_IMAGE_NAME:-container-registry.oracle.com/database/enterprise} export DB_IMAGE_TAG=${DB_IMAGE_TAG:-12.2.0.1-slim} export DB_IMAGE_PULL_SECRET=${DB_IMAGE_PULL_SECRET:-docker-secret} +export DB_PDB_ID=${DB_PDB_ID:-devpdb.k8s} # ::: Traefik settings/defaults, set NODEPORT values to 0 to have # K8S dynamically choose the values for Traefik @@ -65,4 +66,4 @@ export KUBERNETES_CLI=${KUBERNETES_CLI:-kubectl} # default WLSIMG_BUILDER export WLSIMG_BUILDER=${WLSIMG_BUILDER:-docker} -export WLSIMG_BUILDER_DEFAULT=${WLSIMG_BUILDER_DEFAULT:-docker} \ No newline at end of file +export WLSIMG_BUILDER_DEFAULT=${WLSIMG_BUILDER_DEFAULT:-docker} diff --git a/operator/integration-tests/domain-on-pv/util-misc.sh b/operator/integration-tests/domain-on-pv/util-misc.sh index e0589c5c155..d176037a477 100755 --- a/operator/integration-tests/domain-on-pv/util-misc.sh +++ b/operator/integration-tests/domain-on-pv/util-misc.sh @@ -295,7 +295,9 @@ doCommand() { printdots_start set +e - eval $command > $out_file 2>&1 + EXPANDED_CMD=$(eval "echo $command") + echo "$EXPANDED_CMD" + eval $command 2>&1 | tee "$out_file" local err_code=$? printdots_end if [ $err_code -ne 0 ]; then From 3fb568ee572d13b23d149d48c7cbb94acacee7fd Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 14 May 2025 08:42:30 -0400 Subject: [PATCH 335/356] Resiliency for namespace listing and additional liveness checks --- .../oracle/kubernetes/operator/BaseMain.java | 6 +++-- .../operator/DeploymentLiveness.java | 11 +++++++--- .../kubernetes/operator/DomainRecheck.java | 22 ++++++++++++++----- .../kubernetes/operator/JobWatcher.java | 4 ++-- .../kubernetes/operator/OperatorMain.java | 11 ++++++---- .../kubernetes/operator/WebhookMain.java | 9 +++++--- .../operator/helpers/JobHelper.java | 4 ++-- .../operator/helpers/ResponseStep.java | 13 ++++++----- .../operator/DeploymentLivenessTest.java | 9 ++++---- 9 files changed, 57 insertions(+), 32 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java b/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java index e0d4ef94236..a79f1da1f90 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java +++ b/operator/src/main/java/oracle/kubernetes/operator/BaseMain.java @@ -17,9 +17,11 @@ import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; import java.time.OffsetDateTime; +import java.util.Collection; import java.util.Optional; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -159,14 +161,14 @@ public void run() { } } - void markReadyAndStartLivenessThread() { + void markReadyAndStartLivenessThread(Collection> futures) { try { new DeploymentReady(delegate).create(); logStartingLivenessMessage(); // every five seconds we need to update the last modified time on the liveness file wrappedExecutorService.scheduleWithFixedDelay( - new DeploymentLiveness(delegate), 5, 5, TimeUnit.SECONDS); + new DeploymentLiveness(futures, delegate), 5, 5, TimeUnit.SECONDS); } catch (IOException io) { LOGGER.severe(MessageKeys.EXCEPTION, io); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/DeploymentLiveness.java b/operator/src/main/java/oracle/kubernetes/operator/DeploymentLiveness.java index 4491b584a54..8be1c4ed3ba 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DeploymentLiveness.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DeploymentLiveness.java @@ -1,11 +1,13 @@ -// Copyright (c) 2017, 2022, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; import java.io.File; import java.io.IOException; +import java.util.Collection; import java.util.Date; +import java.util.concurrent.ScheduledFuture; import oracle.kubernetes.common.logging.MessageKeys; import oracle.kubernetes.operator.logging.LoggingFacade; @@ -18,9 +20,11 @@ public class DeploymentLiveness implements Runnable { private static final LoggingFacade LOGGER = LoggingFactory.getLogger("Operator", "Operator"); + private final Collection> futures; private final File livenessFile; - public DeploymentLiveness(CoreDelegate delegate) { + public DeploymentLiveness(Collection> futures, CoreDelegate delegate) { + this.futures = futures; livenessFile = new File(delegate.getProbesHome(), ".alive"); } @@ -33,7 +37,8 @@ public void run() { } catch (IOException ioe) { LOGGER.warning(MessageKeys.COULD_NOT_CREATE_LIVENESS_FILE); } - if (livenessFile.setLastModified(new Date().getTime())) { + if (futures.stream().filter(ScheduledFuture::isDone).findAny().isEmpty() + && livenessFile.setLastModified(new Date().getTime())) { LOGGER.fine("Liveness file last modified time set"); } } diff --git a/operator/src/main/java/oracle/kubernetes/operator/DomainRecheck.java b/operator/src/main/java/oracle/kubernetes/operator/DomainRecheck.java index a535e8831e8..d24f606476f 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/DomainRecheck.java +++ b/operator/src/main/java/oracle/kubernetes/operator/DomainRecheck.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2023, Oracle and/or its affiliates. +// Copyright (c) 2020, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -35,6 +35,8 @@ import oracle.kubernetes.operator.work.Packet; import oracle.kubernetes.operator.work.Step; +import static oracle.kubernetes.operator.KubernetesConstants.HTTP_GONE; +import static oracle.kubernetes.operator.calls.AsyncRequestStep.FIBER_TIMEOUT; import static oracle.kubernetes.operator.helpers.EventHelper.EventItem.NAMESPACE_WATCHING_STARTED; import static oracle.kubernetes.operator.helpers.NamespaceHelper.getOperatorNamespace; import static oracle.kubernetes.operator.logging.ThreadLoggingContext.setThreadContext; @@ -161,7 +163,14 @@ private NamespaceListResponseStep() { protected NextAction onFailureNoRetry(Packet packet, CallResponse callResponse) { return useBackupStrategy(callResponse) ? doNext(createStartNamespacesStep(Namespaces.getConfiguredDomainNamespaces()), packet) - : super.onFailureNoRetry(packet, callResponse); + : onFailureNoRetryCheckForGone(packet, callResponse); + } + + protected NextAction onFailureNoRetryCheckForGone(Packet packet, CallResponse callResponse) { + if (Optional.ofNullable(callResponse).map(CallResponse::getStatusCode).orElse(FIBER_TIMEOUT) == HTTP_GONE) { + return doEnd(packet); + } + return super.onFailureNoRetry(packet, callResponse); } // Returns true if the failure wasn't due to authorization, and we have a list of namespaces to manage. @@ -172,12 +181,13 @@ private boolean useBackupStrategy(CallResponse callResponse) { @Override public NextAction onSuccess(Packet packet, CallResponse callResponse) { final Set namespacesToStart = getNamespacesToStart(callResponse.getResult()); - Namespaces.getFoundDomainNamespaces(packet).addAll(namespacesToStart); + Collection foundDomainNamespaces = Namespaces.getFoundDomainNamespaces(packet); + foundDomainNamespaces.addAll(namespacesToStart); - return doContinueListOrNext(callResponse, packet, createNextSteps(namespacesToStart)); + return doContinueListOrNext(callResponse, packet, () -> createNextSteps(foundDomainNamespaces)); } - private Step createNextSteps(Set namespacesToStartNow) { + private Step createNextSteps(Collection namespacesToStartNow) { if (!namespacesToStartNow.isEmpty()) { List nextSteps = new ArrayList<>(); nextSteps.add(createStartNamespacesStep(namespacesToStartNow)); @@ -190,7 +200,7 @@ private Step createNextSteps(Set namespacesToStartNow) { return current; } - private Step createNamespaceReviewStep(Set namespacesToStartNow) { + private Step createNamespaceReviewStep(Collection namespacesToStartNow) { return RunInParallel.perNamespace(namespacesToStartNow, DomainRecheck.this::createNamespaceReview); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java b/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java index 955cee2dc25..1c2bd7b1d87 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java +++ b/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -317,7 +317,7 @@ public NextAction onSuccess(Packet packet, CallResponse callResponse) if (jobPod == null) { terminationState.remove(packet); - return doContinueListOrNext(callResponse, packet, getNext()); + return doContinueListOrNext(callResponse, packet); } else { terminationState.setFromPod(jobPod); return doNext(getNext(), packet); diff --git a/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java b/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java index e5b0154c549..24dd4b8e95f 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java +++ b/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java @@ -1,4 +1,4 @@ -// Copyright (c) 2017, 2023, Oracle and/or its affiliates. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -13,9 +13,11 @@ import java.time.OffsetDateTime; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -301,10 +303,11 @@ void completeBegin() { // start periodic retry and recheck int recheckInterval = TuningParameters.getInstance().getDomainNamespaceRecheckIntervalSeconds(); int stuckPodInterval = TuningParameters.getInstance().getStuckPodRecheckSeconds(); - mainDelegate.scheduleWithFixedDelay(recheckDomains(), recheckInterval, recheckInterval, TimeUnit.SECONDS); - mainDelegate.scheduleWithFixedDelay(checkStuckPods(), stuckPodInterval, stuckPodInterval, TimeUnit.SECONDS); + Collection> futures = List.of( + mainDelegate.scheduleWithFixedDelay(recheckDomains(), recheckInterval, recheckInterval, TimeUnit.SECONDS), + mainDelegate.scheduleWithFixedDelay(checkStuckPods(), stuckPodInterval, stuckPodInterval, TimeUnit.SECONDS)); - markReadyAndStartLivenessThread(); + markReadyAndStartLivenessThread(futures); } catch (Throwable e) { LOGGER.warning(MessageKeys.EXCEPTION, e); diff --git a/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java b/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java index f3631d22c13..899eb3439de 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java +++ b/operator/src/main/java/oracle/kubernetes/operator/WebhookMain.java @@ -1,11 +1,13 @@ -// Copyright (c) 2022, 2023, Oracle and/or its affiliates. +// Copyright (c) 2022, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; +import java.util.List; import java.util.Optional; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -134,9 +136,10 @@ void completeBegin() { // start periodic recheck of CRD int recheckInterval = TuningParameters.getInstance().getDomainNamespaceRecheckIntervalSeconds(); - delegate.scheduleWithFixedDelay(recheckCrd(), recheckInterval, recheckInterval, TimeUnit.SECONDS); + ScheduledFuture future = + delegate.scheduleWithFixedDelay(recheckCrd(), recheckInterval, recheckInterval, TimeUnit.SECONDS); - markReadyAndStartLivenessThread(); + markReadyAndStartLivenessThread(List.of(future)); } catch (Exception e) { LOGGER.warning(MessageKeys.EXCEPTION, e); diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobHelper.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobHelper.java index 6240a83a552..6bdc7b2beec 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/JobHelper.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/JobHelper.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -818,7 +818,7 @@ public NextAction onSuccess(Packet packet, CallResponse callResponse) .orElse(null); if (jobPod == null) { - return doContinueListOrNext(callResponse, packet, processIntrospectorPodLog(getNext())); + return doContinueListOrNext(callResponse, packet, () -> processIntrospectorPodLog(getNext())); } else if (hasImagePullError(jobPod) || initContainersHaveImagePullError(jobPod)) { return doNext(cleanUpAndReintrospect(getNext()), packet); } else if (isJobPodTimedOut(jobPod)) { diff --git a/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java b/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java index 5d46f81e530..13d28e8e543 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java +++ b/operator/src/main/java/oracle/kubernetes/operator/helpers/ResponseStep.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2024, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.helpers; @@ -7,6 +7,7 @@ import java.net.SocketTimeoutException; import java.util.Collections; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nonnull; @@ -140,7 +141,7 @@ private NextAction getPotentialRetryAction(Packet packet) { * @return Next action for list continue */ protected final NextAction doContinueListOrNext(CallResponse callResponse, Packet packet) { - return doContinueListOrNext(callResponse, packet, getNext()); + return doContinueListOrNext(callResponse, packet, this::getNext); } /** @@ -149,10 +150,10 @@ protected final NextAction doContinueListOrNext(CallResponse callResponse, Pa * * @param callResponse Call response * @param packet Packet - * @param next Next step, if no continuation + * @param next Supplier of next step, if no continuation * @return Next action for list continue */ - protected final NextAction doContinueListOrNext(CallResponse callResponse, Packet packet, Step next) { + protected final NextAction doContinueListOrNext(CallResponse callResponse, Packet packet, Supplier next) { String cont = accessContinue(callResponse.getResult()); if (cont != null) { packet.put(CONTINUE, cont); @@ -161,9 +162,9 @@ protected final NextAction doContinueListOrNext(CallResponse callResponse, Pa return resetRetryStrategyAndReinvokeRequest(packet); } if (callResponse.getResult() instanceof KubernetesListObject kubernetesListObject) { - return doNext(next, packet).withDebugComment(kubernetesListObject, this::toComment); + return doNext(next.get(), packet).withDebugComment(kubernetesListObject, this::toComment); } else { - return doNext(next, packet); + return doNext(next.get(), packet); } } diff --git a/operator/src/test/java/oracle/kubernetes/operator/DeploymentLivenessTest.java b/operator/src/test/java/oracle/kubernetes/operator/DeploymentLivenessTest.java index d3e75180218..18ed8d6bf79 100644 --- a/operator/src/test/java/oracle/kubernetes/operator/DeploymentLivenessTest.java +++ b/operator/src/test/java/oracle/kubernetes/operator/DeploymentLivenessTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2022, Oracle and/or its affiliates. +// Copyright (c) 2022, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator; @@ -8,6 +8,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.logging.Level; @@ -56,7 +57,7 @@ public void tearDown() throws Exception { @Test void whenNoExistingLivenessFile_fileCreated() { - DeploymentLiveness deploymentLiveness = new DeploymentLiveness(coreDelegate); + DeploymentLiveness deploymentLiveness = new DeploymentLiveness(Collections.emptyList(), coreDelegate); deploymentLiveness.run(); File aliveFile = new File(coreDelegate.probesHome, ".alive"); @@ -72,7 +73,7 @@ void whenExistingLivenessFile_onlyLogLastModifiedUpdated() throws IOException { File aliveFile = new File(coreDelegate.probesHome, ".alive"); assertTrue(aliveFile.createNewFile()); - DeploymentLiveness deploymentLiveness = new DeploymentLiveness(coreDelegate); + DeploymentLiveness deploymentLiveness = new DeploymentLiveness(Collections.emptyList(), coreDelegate); deploymentLiveness.run(); assertThat(coreDelegate.probesHome, anExistingDirectory()); @@ -86,7 +87,7 @@ void whenExistingLivenessFile_onlyLogLastModifiedUpdated() throws IOException { void whenCantCreateLivenessFile_logWarning() throws IOException { assertTrue(coreDelegate.probesHome.setWritable(false, false)); - DeploymentLiveness deploymentLiveness = new DeploymentLiveness(coreDelegate); + DeploymentLiveness deploymentLiveness = new DeploymentLiveness(Collections.emptyList(), coreDelegate); deploymentLiveness.run(); assertThat(coreDelegate.probesHome, anExistingDirectory()); From e6d613d47b7dd34879a6fa6638be6dce168ac037 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Wed, 14 May 2025 14:10:19 +0000 Subject: [PATCH 336/356] Merge branch 'edburns/ems7375-docs-freshness-wls-aks-vnet' into 'main' On branch edburns/ems7375-docs-freshness-wls-aks-vnet Make this more suitable... See merge request weblogic-cloud/weblogic-kubernetes-operator!4968 (cherry picked from commit e017674d2b2477a3058b6f53f14bad7c85aae8a5) 8be6dfe7 On branch edburns/ems7375-docs-freshness-wls-aks-vnet Make this more suitable... --- documentation/site/content/managing-domains/aks/_index.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/site/content/managing-domains/aks/_index.md b/documentation/site/content/managing-domains/aks/_index.md index 05bb6c0b21f..37e1cca8207 100644 --- a/documentation/site/content/managing-domains/aks/_index.md +++ b/documentation/site/content/managing-domains/aks/_index.md @@ -13,6 +13,11 @@ This document is the reference for the Azure Marketplace offer for WebLogic Serv To deploy the offer from the Azure portal, see [WebLogic Server on Azure](https://aka.ms/wls-aks-portal). +{{% notice note %}} +With the exception of the resource group, and the username and password for the Oracle Single Sign-On fields, each panel of the offer has been configured with sensible defaults. If you were referred to this guidance by another guidance, it is appropriate to accept the defaults for each panel. +{{% /notice %}} + + {{< img "WLS AKS Marketplace Solution Screenshot" "images/aks-solution.png" >}} {{< readfile file="/samples/azure-kubernetes-service/includes/aks-value-prop.txt" >}} From e34df1ffaf323037ad774e0b54ce97d4fea7695c Mon Sep 17 00:00:00 2001 From: "MARINA.KOGAN" Date: Thu, 15 May 2025 17:33:48 +0000 Subject: [PATCH 337/356] OWLS-128825 - ItWlsMiiLegacySample.testUpate4, ItWlsMiiSample.testUpate4 failed due run-test.sh does not check pod readiness before comparing --- .../weblogic/kubernetes/ItWlsMiiSample.java | 102 ++++++++++-------- .../model-in-image/build-operator.sh | 85 ++++++++------- .../model-in-image/run-test.sh | 26 +++-- 3 files changed, 125 insertions(+), 88 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java index 1fb5d16b316..6c253f40b7a 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItWlsMiiSample.java @@ -31,6 +31,7 @@ import static oracle.weblogic.kubernetes.TestConstants.K8S_NODEPORT_HOST; import static oracle.weblogic.kubernetes.TestConstants.KIND_CLUSTER; import static oracle.weblogic.kubernetes.TestConstants.KIND_REPO; +import static oracle.weblogic.kubernetes.TestConstants.KUBERNETES_CLI; import static oracle.weblogic.kubernetes.TestConstants.OKD; import static oracle.weblogic.kubernetes.TestConstants.RESULTS_ROOT; import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; @@ -87,7 +88,6 @@ class ItWlsMiiSample { @BeforeAll public static void initAll(@Namespaces(3) List namespaces) { logger = getLogger(); - // get a new unique opNamespace logger.info("Creating unique namespace for Operator"); assertNotNull(namespaces.get(0), "Namespace list is null"); @@ -101,42 +101,37 @@ public static void initAll(@Namespaces(3) List namespaces) { assertNotNull(namespaces.get(2), "Namespace list is null"); traefikNamespace = namespaces.get(2); - String miiSampleWorkDir = - RESULTS_ROOT + "/" + domainNamespace + "/model-in-image-sample-work-dir"; + String miiSampleWorkDir = RESULTS_ROOT + "/" + domainNamespace + "/model-in-image-sample-work-dir"; - // env variables to override default values in sample scripts envMap = new HashMap<>(); envMap.put("OPER_NAMESPACE", opNamespace); envMap.put("DOMAIN_NAMESPACE", domainNamespace); envMap.put("DOMAIN_UID1", getUniqueName("sample-domain1-")); envMap.put("DOMAIN_UID2", getUniqueName("sample-domain2-")); envMap.put("TRAEFIK_NAMESPACE", traefikNamespace); - envMap.put("TRAEFIK_HTTP_NODEPORT", "0"); // 0-->dynamically choose the np - envMap.put("TRAEFIK_HTTPS_NODEPORT", "0"); // 0-->dynamically choose the np + envMap.put("TRAEFIK_HTTP_NODEPORT", "0");// 0-->dynamically choose the np + envMap.put("TRAEFIK_HTTPS_NODEPORT", "0");// 0-->dynamically choose the np envMap.put("TRAEFIK_NAME", TRAEFIK_RELEASE_NAME + "-" + traefikNamespace.substring(3)); envMap.put("TRAEFIK_IMAGE_REGISTRY", TRAEFIK_INGRESS_IMAGE_REGISTRY); envMap.put("TRAEFIK_IMAGE_REPOSITORY", TRAEFIK_INGRESS_IMAGE_NAME); envMap.put("TRAEFIK_IMAGE_TAG", TRAEFIK_INGRESS_IMAGE_TAG); envMap.put("WORKDIR", miiSampleWorkDir); - envMap.put("BASE_IMAGE_NAME", WEBLOGIC_IMAGE_TO_USE_IN_SPEC - .substring(0, WEBLOGIC_IMAGE_TO_USE_IN_SPEC.lastIndexOf(":"))); + envMap.put("BASE_IMAGE_NAME", WEBLOGIC_IMAGE_TO_USE_IN_SPEC.substring(0, + WEBLOGIC_IMAGE_TO_USE_IN_SPEC.lastIndexOf(":"))); envMap.put("BASE_IMAGE_TAG", WEBLOGIC_IMAGE_TAG); envMap.put("IMAGE_PULL_SECRET_NAME", BASE_IMAGES_REPO_SECRET_NAME); envMap.put("DOMAIN_IMAGE_PULL_SECRET_NAME", TEST_IMAGES_REPO_SECRET_NAME); envMap.put("WLSIMG_BUILDER_DEFAULT", WLSIMG_BUILDER_DEFAULT); envMap.put("WLSIMG_BUILDER", WLSIMG_BUILDER); - envMap.put("OKD", "" + OKD); - envMap.put("KIND_CLUSTER", "" + KIND_CLUSTER); + envMap.put("OKD", String.valueOf(OKD)); + envMap.put("KIND_CLUSTER", String.valueOf(KIND_CLUSTER)); - // kind cluster uses openjdk which is not supported by image tool if (WIT_JAVA_HOME != null) { envMap.put("JAVA_HOME", WIT_JAVA_HOME); } - if (WIT_DOWNLOAD_URL != null) { envMap.put("WIT_INSTALLER_URL", WIT_DOWNLOAD_URL); } - if (WDT_DOWNLOAD_URL != null) { envMap.put("WDT_INSTALLER_URL", WDT_DOWNLOAD_URL); } @@ -146,23 +141,16 @@ public static void initAll(@Namespaces(3) List namespaces) { envMap.put("OPER_IMAGE_NAME", "localhost/weblogic-kubernetes-operator"); envMap.put("MODEL_IMAGE_NAME", DOMAIN_CREATION_IMAGE_NAME); envMap.put("K8S_NODEPORT_HOST", assertDoesNotThrow(() -> InetAddress.getLocalHost().getHostAddress())); - envMap.put("TRAEFIK_INGRESS_HTTP_HOSTPORT", "" + TRAEFIK_INGRESS_HTTP_HOSTPORT); - envMap.put("TRAEFIK_NAMESPACE", TRAEFIK_NAMESPACE); + envMap.put("TRAEFIK_INGRESS_HTTP_HOSTPORT", String.valueOf(TRAEFIK_INGRESS_HTTP_HOSTPORT)); } else { - envMap.put("TRAEFIK_NAMESPACE", traefikNamespace); envMap.put("K8S_NODEPORT_HOST", K8S_NODEPORT_HOST); } - + logger.info("Environment variables to the script {0}", envMap); - logger.info("Setting up image registry secrets"); - // Create the repo secret to pull the domain image - // this secret is used only for non-kind cluster createTestRepoSecret(domainNamespace); logger.info("Registry secret {0} created for domain image successfully in namespace {1}", TEST_IMAGES_REPO_SECRET_NAME, domainNamespace); - // Create the repo secret to pull the base image - // this secret is used only for non-kind cluster createBaseRepoSecret(domainNamespace); logger.info("Registry secret {0} for base image created successfully in namespace {1}", BASE_IMAGES_REPO_SECRET_NAME, domainNamespace); @@ -274,39 +262,63 @@ public void testUpate4() { * @param arg arguments to execute script * @param errString a string of detailed error */ - private void execTestScriptAndAssertSuccess(String arg, - String errString) { - + private void execTestScriptAndAssertSuccess(String arg, String errString) { Assumptions.assumeTrue(previousTestSuccessful); previousTestSuccessful = false; - String command = miiSampleScript - + " " - + arg; - - ExecResult result = Command.withParams( - new CommandParams() - .command(command) - .env(envMap) - .redirect(true) - ).executeAndReturnResult(); + Map podSnapshotBefore = null; + if ("-update4".equals(arg)) { + podSnapshotBefore = getPodTimestamps(envMap.get("DOMAIN_NAMESPACE")); + } - boolean success = - result != null - && result.exitValue() == 0 - && result.stdout() != null - && result.stdout().contains("Finished without errors"); + String command = miiSampleScript + " " + arg; + ExecResult result = Command.withParams(new CommandParams().command(command) + .env(envMap).redirect(true)).executeAndReturnResult(); + + boolean success = result != null && result.exitValue() == 0 && result.stdout() != null + && result.stdout().contains("Finished without errors"); + + if (success && "-update4".equals(arg)) { + Map podSnapshotAfter = getPodTimestamps(envMap.get("DOMAIN_NAMESPACE")); + for (Map.Entry entry : podSnapshotBefore.entrySet()) { + String pod = entry.getKey(); + String beforeTime = entry.getValue(); + String afterTime = podSnapshotAfter.get(pod); + if (afterTime != null && !beforeTime.equals(afterTime)) { + success = false; + logger.severe("Unexpected pod restart detected for pod " + + pod + ": before=" + beforeTime + " after=" + afterTime); + } + } + } - String outStr = errString; - outStr += ", command=\n{\n" + command + "\n}\n"; - outStr += ", stderr=\n{\n" + (result != null ? result.stderr() : "") + "\n}\n"; - outStr += ", stdout=\n{\n" + (result != null ? result.stdout() : "") + "\n}\n"; + String outStr = errString + ", command=\n{\n" + command + "\n}\n" + + ", stderr=\n{\n" + (result != null ? result.stderr() : "") + "\n}\n" + + ", stdout=\n{\n" + (result != null ? result.stdout() : "") + "\n}\n"; assertTrue(success, outStr); - previousTestSuccessful = true; } + private Map getPodTimestamps(String namespace) { + ExecResult result = Command.withParams(new CommandParams() + .command(KUBERNETES_CLI + " get pods -n " + namespace + + " -o=jsonpath='{range .items[*]}{.metadata.name}:{.metadata.creationTimestamp}\\n{end}'") + .redirect(true)).executeAndReturnResult(); + + Map timestamps = new HashMap<>(); + if (result != null && result.exitValue() == 0 && result.stdout() != null) { + String[] lines = result.stdout().replace("'", "").split("\n"); + for (String line : lines) { + String[] parts = line.trim().split(":"); + if (parts.length == 2) { + timestamps.put(parts[0], parts[1]); + } + } + } + return timestamps; + } + /** * Uninstall Traefik. */ diff --git a/operator/integration-tests/model-in-image/build-operator.sh b/operator/integration-tests/model-in-image/build-operator.sh index 57e244706fe..0771cffc4be 100755 --- a/operator/integration-tests/model-in-image/build-operator.sh +++ b/operator/integration-tests/model-in-image/build-operator.sh @@ -1,12 +1,12 @@ #!/bin/bash -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. +# Copyright (c) 2018, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # 'build-wl-operator.sh' # -# Build and helm install an operator that monitors DOMAIN_NAMESPACE. -# +# Build and helm install an operator that monitors DOMAIN_NAMESPACE. +# # This script is not necessary if the operator is already running # and monitoring DOMAIN_NAMESPACE. # @@ -19,63 +19,76 @@ set -o pipefail TESTDIR="$( cd "$(dirname "$0")" > /dev/null 2>&1 ; pwd -P )" SRCDIR="$( cd "$TESTDIR/../../.." > /dev/null 2>&1 ; pwd -P )" +cd "${SRCDIR}" -cd ${SRCDIR} - -echo "build Operator image" +echo "Building Operator image..." +# Environment and defaults OPER_IMAGE_TAG=${OPER_IMAGE_TAG:-test} OPER_IMAGE_NAME=${OPER_IMAGE_NAME:-weblogic-kubernetes-operator} -OPER_JAR_VERSION="`grep -m1 "" pom.xml | cut -f2 -d">" | cut -f1 -d "<"`" +OPER_JAR_VERSION="$(grep -m1 '' pom.xml | cut -f2 -d'>' | cut -f1 -d'<')" -echo "OPER_IMAGE_NAME=$OPER_IMAGE_NAME" -echo "OPER_IMAGE_TAG=$OPER_IMAGE_TAG" -echo "OPER_JAR_VERSION=$OPER_JAR_VERSION" +echo "OPER_IMAGE_NAME=${OPER_IMAGE_NAME}" +echo "OPER_IMAGE_TAG=${OPER_IMAGE_TAG}" +echo "OPER_JAR_VERSION=${OPER_JAR_VERSION}" +# Generate checksum based on content latest_cksum() { # force a rebuild even if only image name/tag/ver changes... echo "$OPER_IMAGE_NAME $OPER_IMAGE_TAG $OPER_JAR_VERSION" # force a rebuild if the image isn't cached anymore - ${WLSIMG_BUILDER:-docker} images $OPER_IMAGE_NAME:$OPER_IMAGE_TAG -q - + ${WLSIMG_BUILDER:-docker} images "$OPER_IMAGE_NAME:$OPER_IMAGE_TAG" -q || true # force a rebuild if any .java, .sh, or .py file changed - find "$SRCDIR/operator/src/main" -name "*.[jsp]*" | xargs cat | cksum - find "$SRCDIR/operator/src/test" -name "*.[jsp]*" | xargs cat | cksum + find "$SRCDIR/operator/src/main" "$SRCDIR/operator/src/test" -type f \( -name "*.java" -o -name "*.sh" -o -name "*.py" \) -exec cat {} + | cksum } save_cksum() { - latest_cksum > $SRCDIR/operator/src/main.cksum + latest_cksum > "$SRCDIR/operator/src/main.cksum" } old_cksum() { - [ -f $SRCDIR/operator/src/main.cksum ] && cat $SRCDIR/operator/src/main.cksum + [ -f "$SRCDIR/operator/src/main.cksum" ] && cat "$SRCDIR/operator/src/main.cksum" } -if [ "$(old_cksum)" = "$(latest_cksum)" ]; then - echo "@@ Info: Skipping oper build/tag - cksum unchanged." +# Avoid rebuild if nothing changed +if [ "$(old_cksum 2>/dev/null)" = "$(latest_cksum)" ]; then + echo "@@ Info: Skipping Operator image build - no changes detected." exit 0 fi -#mvn clean install -DskipTests -Dcheckstyle.skip -mvn clean install -if [ -n "${http_proxy:-}" ]; then - HTTP_BUILD_ARG="--build-arg http_proxy=$http_proxy" -fi -if [ -n "${https_proxy:-}" ]; then - HTTP_BUILD_ARG="$HTTP_BUILD_ARG --build-arg https_proxy=$https_proxy " -fi -if [ -n "${no_proxy:-}" ]; then - HTTP_BUILD_ARG="$HTTP_BUILD_ARG --build-arg no_proxy=$no_proxy" -fi -${WLSIMG_BUILDER:-docker} build ${HTTP_BUILD_ARG:-} -t "$OPER_IMAGE_NAME:$OPER_IMAGE_TAG" --build-arg VERSION=$OPER_JAR_VERSION --no-cache=true . +# Clean up target to avoid file lock issues +echo "@@ Info: Cleaning Maven targets..." +find "$SRCDIR/operator" -name target -type d -exec chmod -R u+w {} \; -exec rm -rf {} + + +# Create unique temp local Maven repo to avoid .m2 lock collisions +TEMP_M2_REPO="/tmp/m2repo-$RANDOM" + +echo "@@ Info: Running Maven build..." +mvn clean install -Dmaven.repo.local="$TEMP_M2_REPO" + +# Handle build args for proxy if set +HTTP_BUILD_ARG="" +[ -n "${http_proxy:-}" ] && HTTP_BUILD_ARG+=" --build-arg http_proxy=$http_proxy" +[ -n "${https_proxy:-}" ] && HTTP_BUILD_ARG+=" --build-arg https_proxy=$https_proxy" +[ -n "${no_proxy:-}" ] && HTTP_BUILD_ARG+=" --build-arg no_proxy=$no_proxy" + + +${WLSIMG_BUILDER:-docker} build ${HTTP_BUILD_ARG:-} \ + -t "${OPER_IMAGE_NAME}:${OPER_IMAGE_TAG}" \ + --build-arg VERSION="${OPER_JAR_VERSION}" \ + --no-cache=true . save_cksum -# push to remote repo if cluster is remote -# if [ -z "$REPO_REGISTRY" ] || [ -z "$REPO_USERNAME" ] || [ -z "$REPO_PASSWORD" ]; then -# echo "Provide container registry login details using REPO_REGISTRY, REPO_USERNAME & REPO_PASSWORD env variables to push the Operator image to the repository." -# exit 1 +# Optional: push image to remote registry +# Uncomment if needed and set REPO_REGISTRY, REPO_USERNAME, REPO_PASSWORD +# if [[ -n "${REPO_REGISTRY:-}" && -n "${REPO_USERNAME:-}" && -n "${REPO_PASSWORD:-}" ]]; then +# echo "@@ Info: Pushing image to ${REPO_REGISTRY}..." +# ${WLSIMG_BUILDER:-docker} login "$REPO_REGISTRY" -u "$REPO_USERNAME" -p "$REPO_PASSWORD" +# ${WLSIMG_BUILDER:-docker} push "${OPER_IMAGE_NAME}:${OPER_IMAGE_TAG}" +# else +# echo "@@ Warning: Skipping ${WLSIMG_BUILDER:-docker} push. Set REPO_REGISTRY, REPO_USERNAME, and REPO_PASSWORD to enable." # fi -# ${WLSIMG_BUILDER:-docker} login $REPO_REGISTRY -u $REPO_USERNAME -p $REPO_PASSWORD -# ${WLSIMG_BUILDER:-docker} push ${IMAGE_NAME_OPERATOR}:${IMAGE_TAG_OPERATOR} + +echo "@@ Done: Operator image built and tagged successfully." diff --git a/operator/integration-tests/model-in-image/run-test.sh b/operator/integration-tests/model-in-image/run-test.sh index d1615648f6a..885dfae6993 100755 --- a/operator/integration-tests/model-in-image/run-test.sh +++ b/operator/integration-tests/model-in-image/run-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2020, 2024, Oracle and/or its affiliates. +# Copyright (c) 2020, 2025, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # @@ -645,6 +645,10 @@ if [ "$DO_UPDATE4" = "true" ]; then doCommand "\$WORKDIR/model-in-image/utils/patch-introspect-version.sh -d \$DOMAIN_UID -n \$DOMAIN_NAMESPACE" waitForDomain Completed + # Ensure pods are Ready before comparing timestamps + echo "@@ Waiting for all pods to be ready in namespace $DOMAIN_NAMESPACE..." + ${KUBERNETES_CLI:-kubectl} -n $DOMAIN_NAMESPACE wait --for=condition=Ready pods --all --timeout=300s + if [ ! "$DRY_RUN" = "true" ]; then if [ "$KIND_CLUSTER" = "true" ]; then testapp internal cluster-1 v2 "'SampleMinThreads' with configured count: 2" 60 quiet @@ -662,13 +666,21 @@ if [ "$DO_UPDATE4" = "true" ]; then fi podInfoAfter="$(getPodInfo | grep -v introspectVersion)" - if [ "$podInfoBefore" = "$podInfoAfter" ]; then - trace "No roll detected. Good!" + getPodTimestamps() { + echo "$1" | grep -E 'name=|creationTimestamp=' | sed -e 's/^ *//' | paste - - | sed 's/ /\\n/g' + } + + beforeTimestamps=$(getPodTimestamps "$podInfoBefore") + afterTimestamps=$(getPodTimestamps "$podInfoAfter") + + if [ "$beforeTimestamps" = "$afterTimestamps" ]; then + trace "No roll detected based on pod creation timestamps. Good!" else - dumpInfo - trace "Info: Pods before:" && echo "${podInfoBefore}" - trace "Info: Pods after:" && echo "${podInfoAfter}" - trace "Error: Unexpected roll detected." + trace "Error: Unexpected roll detected based on creation timestamps." + echo "Before timestamps:" + echo "$beforeTimestamps" + echo "After timestamps:" + echo "$afterTimestamps" exit 1 fi fi From ccb997c2317d18f1e8229e07ae6611c68fbdc02a Mon Sep 17 00:00:00 2001 From: Russell Gold Date: Wed, 14 May 2025 14:54:03 -0400 Subject: [PATCH 338/356] Handle string values in WME configuration --- .../MonitoringExporterConfiguration.java | 66 ++++++++++++------- .../MonitoringExporterConfigurationTest.java | 43 ++++++------ 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java index 783cb887310..17f450803b6 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java @@ -19,6 +19,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; +import jakarta.validation.constraints.NotNull; import oracle.kubernetes.json.Default; import oracle.kubernetes.json.Description; import org.apache.commons.lang3.builder.EqualsBuilder; @@ -96,6 +97,8 @@ static class ExporterQuery extends HashMap { @Description("The attributes for which metrics are to be output. If not specified and a prefix is defined, " + "all values on the MBean will be selected.") private String[] values; + + private Map stringValues; } // This class controls serialization of the exporter configuration. @@ -136,6 +139,7 @@ private void writeQuery(JsonWriter out, ExporterQuery src) throws IOException { writeOptionalStringField(out, "type", src.type); writeOptionalStringField(out, "prefix", src.prefix); writeOptionalValueArray(out, src.values); + writeOptionalStringValues(out, src.stringValues); for (Map.Entry entry : src.entrySet()) { out.name(entry.getKey()); @@ -153,16 +157,31 @@ private void writeOptionalStringField(JsonWriter out, String name, @Nullable Str private void writeOptionalValueArray(JsonWriter out, @Nullable String[] values) throws IOException { if (values != null && values.length > 0) { - out.name("values"); - out.beginArray(); - for (String value : values) { - out.value(value); + writeArray(out, "values", values); + } + } + + private static void writeArray(JsonWriter out, String name, @NotNull String [] values) throws IOException { + out.name(name); + out.beginArray(); + for (String value : values) { + out.value(value); + } + out.endArray(); + } + + private void writeOptionalStringValues(JsonWriter out, @Nullable Map stringValues) throws IOException { + if (stringValues != null && !stringValues.isEmpty()) { + out.name("stringValues"); + out.beginObject(); + for (String key: stringValues.keySet()) { + writeArray(out, key, stringValues.get(key)); } - out.endArray(); + out.endObject(); } } - @Override + @Override public MonitoringExporterConfiguration read(JsonReader in) throws IOException { MonitoringExporterConfiguration configuration = new MonitoringExporterConfiguration(); in.beginObject(); @@ -202,23 +221,13 @@ private ExporterQuery readQuery(JsonReader in) throws IOException { while (in.hasNext()) { String name = in.nextName(); switch (name) { - case "key": - query.key = in.nextString(); - break; - case "keyName": - query.keyName = in.nextString(); - break; - case "type": - query.type = in.nextString(); - break; - case "prefix": - query.prefix = in.nextString(); - break; - case "values": - query.values = readArrayValue(in); - break; - default: - query.put(name, readQuery(in)); + case "key" -> query.key = in.nextString(); + case "keyName" -> query.keyName = in.nextString(); + case "type" -> query.type = in.nextString(); + case "prefix" -> query.prefix = in.nextString(); + case "values" -> query.values = readArrayValue(in); + case "stringValues" -> query.stringValues = readStringValues(in); + default -> query.put(name, readQuery(in)); } } in.endObject(); @@ -242,6 +251,17 @@ private void readStringArray(JsonReader in, List result) throws IOExcept } in.endArray(); } + + private Map readStringValues(JsonReader in) throws IOException { + Map result = new HashMap<>(); + in.beginObject(); + while (in.hasNext()) { + String name = in.nextName(); + result.put(name, readArrayValue(in)); + } + in.endObject(); + return result; + } } @Override diff --git a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfigurationTest.java b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfigurationTest.java index 46bf5998014..2c8c643ed73 100644 --- a/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfigurationTest.java +++ b/operator/src/test/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfigurationTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021, Oracle and/or its affiliates. +// Copyright (c) 2021, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.weblogic.domain.model; @@ -35,26 +35,31 @@ void tearDown() { void deserializeFromJson() { final MonitoringExporterConfiguration configuration = MonitoringExporterConfiguration.createFromYaml(CONFIG); - assertThat(configuration.asJsonString(), hasJsonPath("$.metricsNameSnakeCase", equalTo(true))); - assertThat(configuration.asJsonString(), hasJsonPath("$.queries[0].applicationRuntimes.key", equalTo("name"))); - assertThat(configuration.asJsonString(), - hasJsonPath("$.queries[0].applicationRuntimes.componentRuntimes.type", equalTo("WebAppComponentRuntime"))); + final String jsonString = configuration.asJsonString(); + assertThat(jsonString, hasJsonPath("$.metricsNameSnakeCase", equalTo(true))); + assertThat(jsonString, hasJsonPath("$.queries[0].applicationRuntimes.key", equalTo("name"))); + assertThat(jsonString, hasJsonPath("$.queries[0].applicationRuntimes.componentRuntimes.type", equalTo("WebAppComponentRuntime"))); + assertThat(configuration.matchesYaml(CONFIG), is(true)); } - private static final String CONFIG = "---\n" - + "metricsNameSnakeCase: true\n" - + "queries:\n" - + "- applicationRuntimes:\n" - + " key: name\n" - + " componentRuntimes:\n" - + " type: WebAppComponentRuntime\n" - + " prefix: webapp_config_\n" - + " key: name\n" - + " values: [deploymentState, type, contextRoot, sourceInfo, openSessionsHighCount]\n" - + " servlets:\n" - + " prefix: weblogic_servlet_\n" - + " key: servletName\n" - + " values: [invocationTotalCount, executionTimeTotal]\n"; + private static final String CONFIG = """ + --- + metricsNameSnakeCase: true + queries: + - applicationRuntimes: + key: name + componentRuntimes: + type: WebAppComponentRuntime + prefix: webapp_config_ + key: name + values: [deploymentState, type, contextRoot, sourceInfo, openSessionsHighCount] + stringValues: + state: [ok,failed,overloaded,critical,warn] + servlets: + prefix: weblogic_servlet_ + key: servletName + values: [invocationTotalCount, executionTimeTotal] + """; @Test void matchVisuallyDifferentYaml() { From acbcb6bdd3aa15f9842f5f50545f247aedd0ea52 Mon Sep 17 00:00:00 2001 From: Russell Gold Date: Wed, 14 May 2025 15:13:29 -0400 Subject: [PATCH 339/356] Handle line-length and indentation errors --- .../domain/model/MonitoringExporterConfiguration.java | 4 ++-- .../domain/model/MonitoringExporterConfigurationTest.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java index 17f450803b6..5965aedf1f2 100644 --- a/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java +++ b/operator/src/main/java/oracle/kubernetes/weblogic/domain/model/MonitoringExporterConfiguration.java @@ -170,7 +170,7 @@ private static void writeArray(JsonWriter out, String name, @NotNull String [] v out.endArray(); } - private void writeOptionalStringValues(JsonWriter out, @Nullable Map stringValues) throws IOException { + private void writeOptionalStringValues(JsonWriter out, Map stringValues) throws IOException { if (stringValues != null && !stringValues.isEmpty()) { out.name("stringValues"); out.beginObject(); @@ -181,7 +181,7 @@ private void writeOptionalStringValues(JsonWriter out, @Nullable Map Date: Thu, 15 May 2025 11:50:04 -0400 Subject: [PATCH 340/356] Add namespace-level metrics --- .../operator/NamespaceCollector.java | 43 +++++++++++++++++++ .../kubernetes/operator/OperatorMain.java | 1 + 2 files changed, 44 insertions(+) create mode 100644 operator/src/main/java/oracle/kubernetes/operator/NamespaceCollector.java diff --git a/operator/src/main/java/oracle/kubernetes/operator/NamespaceCollector.java b/operator/src/main/java/oracle/kubernetes/operator/NamespaceCollector.java new file mode 100644 index 00000000000..a91e1806e2e --- /dev/null +++ b/operator/src/main/java/oracle/kubernetes/operator/NamespaceCollector.java @@ -0,0 +1,43 @@ +// Copyright (c) 2025, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.kubernetes.operator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import io.prometheus.client.Collector; +import io.prometheus.client.GaugeMetricFamily; +import oracle.kubernetes.operator.helpers.DomainPresenceInfo; + +public class NamespaceCollector extends Collector { + private static final String WKO_NAMESPACE_COUNT = "wko_namespace_count"; + private static final String WKO_DOMAIN_COUNT = "wko_domain_count"; + + private final MainDelegate mainDelegate; + + public NamespaceCollector(MainDelegate mainDelegate) { + this.mainDelegate = mainDelegate; + } + + @Override + public List collect() { + List mfs = new ArrayList<>(); + Set namespaces = mainDelegate.getDomainNamespaces().getNamespaces(); + mfs.add(new GaugeMetricFamily(WKO_NAMESPACE_COUNT, "Count of managed namespaces", namespaces.size())); + Map> dps = mainDelegate.getDomainProcessor().getDomainPresenceInfoMap(); + GaugeMetricFamily domains = new GaugeMetricFamily( + WKO_DOMAIN_COUNT, "Count of WebLogic domains", Collections.singletonList("namespace")); + mfs.add(domains); + for (String ns : namespaces) { + Map dp = dps.get(ns); + int size = Optional.ofNullable(dp).map(Map::size).orElse(0); + domains.addMetric(Collections.singletonList(ns), size); + } + return mfs; + } +} diff --git a/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java b/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java index 24dd4b8e95f..55453b09ed2 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java +++ b/operator/src/main/java/oracle/kubernetes/operator/OperatorMain.java @@ -297,6 +297,7 @@ public NextAction onSuccess(Packet packet, CallResponse callRes void completeBegin() { try { + new NamespaceCollector(mainDelegate).register(); startMetricsServer(container); startRestServer(container); From 20db264a8cd1d220f18154176290a218dbca30fd Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 16 May 2025 08:49:16 -0400 Subject: [PATCH 341/356] Dependency updates --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aacebcb4102..dab77a30c65 100644 --- a/pom.xml +++ b/pom.xml @@ -732,7 +732,7 @@ 4.3.0 19.0.2 3.0.1u2 - 2.1.20 + 2.1.21 4.12.0 3.11.0 1.80 @@ -753,7 +753,7 @@ 12.1.1 2.0.17 1.5.18 - 4.30.2 + 4.31.0 2.5.2 10.3 ${project.basedir}/src-generated-swagger From b70f2e0847a9cac23a50db693f26cf46006e44e6 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 16 May 2025 09:10:08 -0400 Subject: [PATCH 342/356] Prepare for WKO version 4.2.17 --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 4313de8301a..66404b494b4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index c72fb68c6df..58fdafadd7d 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 9abdedb64ec..fb6b22a23f8 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 9d814500a13..831491b42f3 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.17-SNAPSHOT + 4.2.17 json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 7e67857b95e..94de2887069 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 44ffd52cd24..19c96e1d9a4 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.17-SNAPSHOT + 4.2.17 operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 5beeea3cad3..95a0b8f62b9 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index dab77a30c65..20213ce46c9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index 626c6462120..ce6738573b0 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17-SNAPSHOT + 4.2.17 operator-swagger From d8d50108562e1567164a8165daf984a742ff20c4 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 16 May 2025 10:30:35 -0400 Subject: [PATCH 343/356] Prepare for next development iteration --- common/pom.xml | 2 +- domain-upgrader/pom.xml | 2 +- integration-tests/pom.xml | 2 +- json-schema-generator/pom.xml | 2 +- kubernetes/pom.xml | 2 +- operator-build-maven-plugin/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 2 +- swagger-generator/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/pom.xml b/common/pom.xml index 66404b494b4..4ab3b5ab4bc 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT common diff --git a/domain-upgrader/pom.xml b/domain-upgrader/pom.xml index 58fdafadd7d..c9dfc1b41b1 100644 --- a/domain-upgrader/pom.xml +++ b/domain-upgrader/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT domain-upgrader diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index fb6b22a23f8..3ac1c089277 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT integration-tests diff --git a/json-schema-generator/pom.xml b/json-schema-generator/pom.xml index 831491b42f3..7d14cb4e7e0 100644 --- a/json-schema-generator/pom.xml +++ b/json-schema-generator/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.17 + 4.2.18-SNAPSHOT json-schema diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml index 94de2887069..0a3fe1d4eac 100644 --- a/kubernetes/pom.xml +++ b/kubernetes/pom.xml @@ -9,7 +9,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT installation-tests diff --git a/operator-build-maven-plugin/pom.xml b/operator-build-maven-plugin/pom.xml index 19c96e1d9a4..8d6d3aac24f 100644 --- a/operator-build-maven-plugin/pom.xml +++ b/operator-build-maven-plugin/pom.xml @@ -7,7 +7,7 @@ operator-parent oracle.kubernetes - 4.2.17 + 4.2.18-SNAPSHOT operator-build-maven-plugin diff --git a/operator/pom.xml b/operator/pom.xml index 95a0b8f62b9..fad505d9f79 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT weblogic-kubernetes-operator diff --git a/pom.xml b/pom.xml index 20213ce46c9..4b5922ec585 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT operator diff --git a/swagger-generator/pom.xml b/swagger-generator/pom.xml index ce6738573b0..1c93d8c5d2f 100644 --- a/swagger-generator/pom.xml +++ b/swagger-generator/pom.xml @@ -7,7 +7,7 @@ oracle.kubernetes operator-parent - 4.2.17 + 4.2.18-SNAPSHOT operator-swagger From 7b036b726603d546ebb2ace658376405c1780be9 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Sun, 18 May 2025 11:01:17 -0400 Subject: [PATCH 344/356] Check for job status FailureTarget --- .../main/java/oracle/kubernetes/operator/JobWatcher.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java b/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java index 1c2bd7b1d87..92b62300eba 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java +++ b/operator/src/main/java/oracle/kubernetes/operator/JobWatcher.java @@ -165,7 +165,8 @@ private static List getJobConditions(@Nonnull V1Job job) { } private static boolean isJobConditionFailed(V1JobCondition jobCondition) { - return "Failed".equals(getType(jobCondition)) && getStatus(jobCondition).equals("True"); + return ("FailureTarget".equals(getType(jobCondition)) || "Failed".equals(getType(jobCondition))) + && getStatus(jobCondition).equals("True"); } private static String getType(V1JobCondition jobCondition) { @@ -185,7 +186,8 @@ public static String getFailedReason(V1Job job) { V1JobStatus status = job.getStatus(); if (status != null && status.getConditions() != null) { for (V1JobCondition cond : status.getConditions()) { - if ("Failed".equals(cond.getType()) && "True".equals(cond.getStatus())) { + if (("FailureTarget".equals(cond.getType()) || "Failed".equals(cond.getType())) + && "True".equals(cond.getStatus())) { return cond.getReason(); } } From 597020c09f2751fef67055d9bee7d2a95e4139a5 Mon Sep 17 00:00:00 2001 From: maggie_he Date: Thu, 22 May 2025 19:23:44 +0000 Subject: [PATCH 345/356] Backport ItRunAsUser to release/4.2 --- .../weblogic/kubernetes/ItRunAsUser.java | 419 ++++++++++++++++++ .../weblogic/kubernetes/TestConstants.java | 2 + .../impl/primitive/WebLogicImageTool.java | 8 +- .../extensions/InitializationTasks.java | 2 + 4 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRunAsUser.java diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRunAsUser.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRunAsUser.java new file mode 100644 index 00000000000..567340f9a7c --- /dev/null +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItRunAsUser.java @@ -0,0 +1,419 @@ +// Copyright (c) 2025, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +package oracle.weblogic.kubernetes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import io.kubernetes.client.openapi.models.V1EnvVar; +import io.kubernetes.client.openapi.models.V1LocalObjectReference; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.kubernetes.client.openapi.models.V1PodSecurityContext; +import io.kubernetes.client.openapi.models.V1ResourceRequirements; +import oracle.weblogic.domain.Configuration; +import oracle.weblogic.domain.DomainResource; +import oracle.weblogic.domain.DomainSpec; +import oracle.weblogic.domain.Model; +import oracle.weblogic.domain.ServerPod; +import oracle.weblogic.kubernetes.actions.impl.primitive.Command; +import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; +import oracle.weblogic.kubernetes.annotations.IntegrationTest; +import oracle.weblogic.kubernetes.annotations.Namespaces; +import oracle.weblogic.kubernetes.logging.LoggingFacade; +import org.awaitility.core.ConditionFactory; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_PASSWORD_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.ADMIN_USERNAME_DEFAULT; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_PASSWORD; +import static oracle.weblogic.kubernetes.TestConstants.BASE_IMAGES_REPO_USERNAME; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_API_VERSION; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_PREFIX; +import static oracle.weblogic.kubernetes.TestConstants.DOMAIN_IMAGES_REPO; +import static oracle.weblogic.kubernetes.TestConstants.IMAGE_PULL_POLICY; +import static oracle.weblogic.kubernetes.TestConstants.KIND_REPO; +import static oracle.weblogic.kubernetes.TestConstants.MANAGED_SERVER_NAME_BASE; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_APP_NAME; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_DOMAINTYPE; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.MII_BASIC_WDT_MODEL_FILE; +import static oracle.weblogic.kubernetes.TestConstants.OKD; +import static oracle.weblogic.kubernetes.TestConstants.SKIP_BUILD_IMAGES_IF_EXISTS; +import static oracle.weblogic.kubernetes.TestConstants.TEST_IMAGES_REPO_SECRET_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_IMAGE_DOMAINHOME; +import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_WLSADM_TAG; +import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; +import static oracle.weblogic.kubernetes.TestConstants.WLS_DOMAIN_TYPE; +import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.MODEL_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WDT_VERSION; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_BUILD_DIR; +import static oracle.weblogic.kubernetes.actions.ActionConstants.WIT_JAVA_HOME; +import static oracle.weblogic.kubernetes.actions.TestActions.buildAppArchive; +import static oracle.weblogic.kubernetes.actions.TestActions.createImage; +import static oracle.weblogic.kubernetes.actions.TestActions.defaultAppParams; +import static oracle.weblogic.kubernetes.actions.TestActions.defaultWitParams; +import static oracle.weblogic.kubernetes.actions.TestActions.imagePush; +import static oracle.weblogic.kubernetes.actions.TestActions.imageRepoLogin; +import static oracle.weblogic.kubernetes.assertions.TestAssertions.doesImageExist; +import static oracle.weblogic.kubernetes.assertions.TestAssertions.imageExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.DomainUtils.createDomainAndVerify; +import static oracle.weblogic.kubernetes.utils.FileUtils.checkDirectory; +import static oracle.weblogic.kubernetes.utils.ImageUtils.createTestRepoSecret; +import static oracle.weblogic.kubernetes.utils.OperatorUtils.installAndVerifyOperator; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodExists; +import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodReady; +import static oracle.weblogic.kubernetes.utils.PodUtils.setPodAntiAffinity; +import static oracle.weblogic.kubernetes.utils.SecretUtils.createSecretWithUsernamePassword; +import static oracle.weblogic.kubernetes.utils.ThreadSafeLogger.getLogger; +import static org.awaitility.Awaitility.with; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Create a custom 14.1.2.0 WebLogic base image with added new user, wlsadm:root (with uid 12345). + * 1. Create a file called additionalBuildCommands + * [initial-build-commands] + * RUN useradd -u 12345 wlsadm -g root + * RUN chown -R wlsadm:root /u01 + * + * [final-build-commands] + * USER wlsadm + * 2. Create the custom image based on the default base Weblogic image + * imagetool create \ + * --fromImage phx.ocir.io/devweblogic/test-images/weblogic:14.1.2.0-generic-jdk17-ol8 \ + * --tag phx.ocir.io/devweblogic/test-images/weblogic:14.1.2.0-with-wlsadm-jdk17-ol8 \ + * --type WLS \ + * --version 14.1.2.0 \ + * --additionalBuildCommands pathto/additionalBuildCommands \ + * --chown wlsadm:root + * + * Push this image to the ocir with tag: 14.1.2.0-with-wlsadm-jdk17-ol8-org + * Using this custom WebLogic base image to create a mii domain with new user, wlsadm:root + * Set runAsUser > 10000(12345) and verify mii domain is up and running + */ +@DisplayName("Verify using custom WebLogic base image with added user, wlsadm: root, wko supports runAsUser > 10000") +@IntegrationTest +@Tag("kind-sequential") +class ItRunAsUser { + + private static String opNamespace; + private static String domainNamespace = null; + + // domain constants + private static final String domainUid = "domain1"; + private static final int replicaCount = 1; + private static final String adminServerPodName = domainUid + "-" + ADMIN_SERVER_NAME_BASE; + private static final String managedServerPrefix = domainUid + "-" + MANAGED_SERVER_NAME_BASE; + private static LoggingFacade logger = null; + + ConditionFactory withVeryLongRetryPolicy + = with().pollDelay(0, SECONDS) + .and().with().pollInterval(10, SECONDS) + .atMost(30, MINUTES).await(); + + + /** + * Get namespaces for operator and WebLogic domain. + * + * @param namespaces list of namespaces created by the IntegrationTestWatcher by the + * JUnit engine parameter resolution mechanism + */ + @BeforeAll + static void initAll(@Namespaces(2) List namespaces) { + logger = getLogger(); + // get a unique operator namespace + logger.info("Getting a unique namespace for operator"); + assertNotNull(namespaces.get(0), "Namespace list is null"); + opNamespace = namespaces.get(0); + + // get a unique domain namespace + logger.info("Getting a unique namespace for WebLogic domain"); + assertNotNull(namespaces.get(1), "Namespace list is null"); + domainNamespace = namespaces.get(1); + + // install and verify operator + installAndVerifyOperator(opNamespace, domainNamespace); + + } + + /** + * Create a WLS mii domain with user, wlsadmin:root. + * Set runAsUser as 12345. + * Verify Pod is ready and service exists for both admin server and managed servers. + */ + @Test + @DisabledIfEnvironmentVariable(named = "OKD", matches = "true") + @DisplayName("Verify a mii domain with runAsUser as 12345 is up and running") + void testRunAsUserOver10k() { + + String miiImageWlsadmName = DOMAIN_IMAGES_PREFIX + "mii-image-wlsadm"; + String miiImageWlsadm = miiImageWlsadmName + ":" + MII_BASIC_IMAGE_TAG; + + // create mii Image for this test with user wlsadm: root + createMiiImage(miiImageWlsadmName, miiImageWlsadm); + + // create registry secret to pull the image from registry + // this secret is used only for non-kind cluster + logger.info("Creating registry secret in namespace {0}", domainNamespace); + createTestRepoSecret(domainNamespace); + + // create secret for admin credentials + logger.info("Creating secret for admin credentials"); + String adminSecretName = "weblogic-credentials"; + createSecretWithUsernamePassword(adminSecretName, domainNamespace, ADMIN_USERNAME_DEFAULT, ADMIN_PASSWORD_DEFAULT); + + // create encryption secret + logger.info("Creating encryption secret"); + String encryptionSecretName = "encryptionsecret"; + createSecretWithUsernamePassword(encryptionSecretName, domainNamespace, "weblogicenc", "weblogicenc"); + + ServerPod srvrPod = new ServerPod() + .addEnvItem(new V1EnvVar() + .name("JAVA_OPTIONS") + .value("-Dweblogic.StdoutDebugEnabled=false")) + .addEnvItem(new V1EnvVar() + .name("USER_MEM_ARGS") + .value("-Djava.security.egd=file:/dev/./urandom ")) + .resources(new V1ResourceRequirements() + .limits(new HashMap<>()) + .requests(new HashMap<>())); + + if (!OKD) { + V1PodSecurityContext podSecCtxt = new V1PodSecurityContext() + .runAsUser(12345L); + srvrPod.podSecurityContext(podSecCtxt); + + } + + // create the domain CR + DomainResource domain = new DomainResource() + .apiVersion(DOMAIN_API_VERSION) + .kind("Domain") + .metadata(new V1ObjectMeta() + .name(domainUid) + .namespace(domainNamespace)) + .spec(new DomainSpec() + .domainUid(domainUid) + .domainHomeSourceType("FromModel") + .image(miiImageWlsadm) + .imagePullPolicy(IMAGE_PULL_POLICY) + .addImagePullSecretsItem(new V1LocalObjectReference() + .name(TEST_IMAGES_REPO_SECRET_NAME)) + .webLogicCredentialsSecret(new V1LocalObjectReference() + .name(adminSecretName)) + .includeServerOutInPodLog(true) + .serverStartPolicy("IfNeeded") + .serverPod(srvrPod) + .configuration(new Configuration() + .introspectorJobActiveDeadlineSeconds(3000L) + .model(new Model() + .domainType(WLS_DOMAIN_TYPE) + .runtimeEncryptionSecret(encryptionSecretName)))); + setPodAntiAffinity(domain); + // create model in image domain + logger.info("Creating model in image domain {0} in namespace {1} using image {2}", + domainUid, domainNamespace, miiImageWlsadm); + createDomainAndVerify(domain, domainNamespace); + + // check that admin server pod exists in the domain namespace + logger.info("Checking that admin server pod {0} exists in namespace {1}", + adminServerPodName, domainNamespace); + checkPodExists(adminServerPodName, domainUid, domainNamespace); + + logger.info("Checking that admin service {0} exists in namespace {1}", + adminServerPodName, domainNamespace); + checkServiceExists(adminServerPodName, domainNamespace); + + // check that admin server pod is ready + logger.info("Checking that admin server pod {0} is ready in namespace {1}", + adminServerPodName, domainNamespace); + checkPodReady(adminServerPodName, domainUid, domainNamespace); + + // check for managed server pods existence in the domain namespace + for (int i = 1; i <= replicaCount; i++) { + String managedServerPodName = managedServerPrefix + i; + + // check that the managed server pod exists in the domain namespace + logger.info("Checking that managed server pod {0} exists in namespace {1}", + managedServerPodName, domainNamespace); + checkPodExists(managedServerPodName, domainUid, domainNamespace); + + // check that the managed server service exists in the domain namespace + logger.info("Checking that managed server service {0} exists in namespace {1}", + managedServerPodName, domainNamespace); + checkServiceExists(managedServerPodName, domainNamespace); + + // check that the managed server pod is ready + logger.info("Checking that managed server pod {0} is ready in namespace {1}", + managedServerPodName, domainNamespace); + checkPodReady(managedServerPodName, domainUid, domainNamespace); + } + + } + + private void createMiiImage(String miiImageName, String miiImage) { + + // build MII basic image if does not exits + logger.info("Build/Check mii-basic-wlsadm image with tag {0}", MII_BASIC_IMAGE_TAG); + if (!imageExists(miiImageName, MII_BASIC_IMAGE_TAG)) { + logger.info("Building mii image with name {0}, tag {1}, image {2}", miiImageName, MII_BASIC_IMAGE_TAG, + miiImage); + testUntil( + withVeryLongRetryPolicy, + createBasicImage(miiImageName, MII_BASIC_IMAGE_TAG, MII_BASIC_WDT_MODEL_FILE, + null, MII_BASIC_APP_NAME, MII_BASIC_IMAGE_DOMAINTYPE), + logger, + "create to be successful"); + } else { + logger.info("!!!! domain image {0} exists !!!!", miiImage); + } + + assertTrue(doesImageExist(MII_BASIC_IMAGE_TAG), + String.format("Image %s doesn't exist", miiImage)); + + logger.info(WLSIMG_BUILDER + " login"); + testUntil(withVeryLongRetryPolicy, + () -> imageRepoLogin(BASE_IMAGES_REPO, BASE_IMAGES_REPO_USERNAME, BASE_IMAGES_REPO_PASSWORD), + logger, WLSIMG_BUILDER + " login to BASE_IMAGES_REPO to be successful"); + + // push the images to test images repository + if (!DOMAIN_IMAGES_REPO.isEmpty()) { + + List images = new ArrayList<>(); + + // add images only if SKIP_BUILD_IMAGES_IF_EXISTS is not set + if (!SKIP_BUILD_IMAGES_IF_EXISTS) { + images.add(miiImage); + + } + + for (String image : images) { + if (KIND_REPO != null) { + logger.info("kind load docker-image {0} --name kind", image); + } else { + logger.info(WLSIMG_BUILDER + " push image {0} to {1}", image, DOMAIN_IMAGES_REPO); + } + testUntil( + withVeryLongRetryPolicy, + () -> imagePush(image), + logger, + WLSIMG_BUILDER + " push to TEST_IMAGES_REPO/kind for image {0} to be successful", + image); + } + + // list images for Kind cluster + if (KIND_REPO != null) { + Command + .withParams(new CommandParams() + .command(WLSIMG_BUILDER + " exec kind-worker crictl images") + .verbose(true) + .saveResults(true)) + .execute(); + } + } + + } + + /** + * Create image with basic domain model yaml, variable file and sample application. + * + * @param imageName name of the image + * @param imageTag tag of the image + * @param modelFile model file to build the image + * @param varFile variable file to build the image + * @param appName name of the application to build the image + * @param domainType domain type to be built + * @return true if image is created successfully + */ + + private Callable createBasicImage(String imageName, String imageTag, String modelFile, String varFile, + String appName, String domainType) { + return (() -> { + LoggingFacade logger = getLogger(); + final String image = imageName + ":" + imageTag; + + // build the model file list + final List modelList = Collections.singletonList(MODEL_DIR + "/" + modelFile); + + // build an application archive using what is in resources/apps/APP_NAME + logger.info("Build an application archive using resources/apps/{0}", appName); + assertTrue(buildAppArchive(defaultAppParams() + .srcDirList(Collections.singletonList(appName))), + String.format("Failed to create app archive for %s", appName)); + + // build the archive list + String zipFile = String.format("%s/%s.zip", ARCHIVE_DIR, appName); + final List archiveList = Collections.singletonList(zipFile); + + // Set additional environment variables for WIT + checkDirectory(WIT_BUILD_DIR); + Map env = new HashMap<>(); + env.put("WLSIMG_BLDDIR", WIT_BUILD_DIR); + + if (WIT_JAVA_HOME != null) { + env.put("JAVA_HOME", WIT_JAVA_HOME); + } + + String witTarget = ((OKD) ? "OpenShift" : "Default"); + + // build an image using WebLogic Image Tool + boolean imageCreation = false; + logger.info("Create image {0} using model directory {1}", image, MODEL_DIR); + if (domainType.equalsIgnoreCase("wdt")) { + final List modelVarList = Collections.singletonList(MODEL_DIR + "/" + varFile); + imageCreation = createImage( + defaultWitParams() + .baseImageName(WEBLOGIC_IMAGE_NAME) + .baseImageTag(WEBLOGIC_IMAGE_WLSADM_TAG) + .modelImageName(imageName) + .modelImageTag(WDT_BASIC_IMAGE_TAG) + .modelFiles(modelList) + .modelArchiveFiles(archiveList) + .modelVariableFiles(modelVarList) + .domainHome(WDT_BASIC_IMAGE_DOMAINHOME) + .wdtOperation("CREATE") + .wdtVersion(WDT_VERSION) + .target(witTarget) + .env(env) + .useridGroupid("wlsadm:root") + .redirect(true)); + } else if (domainType.equalsIgnoreCase("mii")) { + imageCreation = createImage( + defaultWitParams() + .baseImageName(WEBLOGIC_IMAGE_NAME) + .baseImageTag(WEBLOGIC_IMAGE_WLSADM_TAG) + .modelImageName(imageName) + .modelImageTag(MII_BASIC_IMAGE_TAG) + .modelFiles(modelList) + .modelArchiveFiles(archiveList) + .wdtModelOnly(true) + .wdtVersion(WDT_VERSION) + .target(witTarget) + .env(env) + .useridGroupid("wlsadm:root") + .redirect(true)); + } + return imageCreation; + }); + } + +} + diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java index b233ebb90fd..b7cabc0069f 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/TestConstants.java @@ -136,6 +136,8 @@ public interface TestConstants { + getNonEmptySystemProperty("wko.it.weblogic.image.name", WEBLOGIC_IMAGE_NAME_DEFAULT); public static final String WEBLOGIC_IMAGE_TAG = getNonEmptySystemProperty("wko.it.weblogic.image.tag", WEBLOGIC_IMAGE_TAG_DEFAULT); + //WebLogic base image tag with added user "wlsadm" for 14.1.2.0-jdk17-ol8 + String WEBLOGIC_IMAGE_WLSADM_TAG = "14.1.2.0-with-wlsadm-jdk17-ol8"; // Get FMWINFRA_IMAGE_NAME/FMWINFRA_IMAGE_TAG from env var, if its not // provided and if base images repo is OCIR use OCIR default image values diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java index 478846c6c79..883b7ed6880 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/actions/impl/primitive/WebLogicImageTool.java @@ -167,7 +167,13 @@ private boolean downloadWdt(String version) { private String buildWitCommand() { LoggingFacade logger = getLogger(); - String ownership = " --chown oracle:root"; + String ownership = null; + + if (params.useridGroupid() == null) { + ownership = " --chown oracle:root"; + } else { + ownership = " --chown " + params.useridGroupid(); + } if (OKE_CLUSTER) { if (params.baseImageName().equals(FMWINFRA_IMAGE_NAME)) { String output = inspectImage(params.baseImageName(), params.baseImageTag()); diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java index e3017335e90..afae13617a4 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/extensions/InitializationTasks.java @@ -87,6 +87,7 @@ import static oracle.weblogic.kubernetes.TestConstants.WDT_BASIC_MODEL_PROPERTIES_FILE; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_NAME; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_TAG; +import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_IMAGE_WLSADM_TAG; import static oracle.weblogic.kubernetes.TestConstants.WEBLOGIC_SHIPHOME; import static oracle.weblogic.kubernetes.TestConstants.WLSIMG_BUILDER; import static oracle.weblogic.kubernetes.actions.ActionConstants.ARCHIVE_DIR; @@ -210,6 +211,7 @@ public void beforeAll(ExtensionContext context) { Collection images = new ArrayList<>(); images.add(WEBLOGIC_IMAGE_NAME + ":" + WEBLOGIC_IMAGE_TAG); + images.add(WEBLOGIC_IMAGE_NAME + ":" + WEBLOGIC_IMAGE_WLSADM_TAG); images.add(FMWINFRA_IMAGE_NAME + ":" + FMWINFRA_IMAGE_TAG); images.add(DB_IMAGE_NAME + ":" + DB_IMAGE_TAG); images.add(LOCALE_IMAGE_NAME + ":" + LOCALE_IMAGE_TAG); From fecf06897e2f6c70ef966639b995416639d133cd Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Fri, 23 May 2025 16:07:47 +0000 Subject: [PATCH 346/356] Merge branch 'k8s132-doc' into 'main' Document certification of k8s 1.32 See merge request weblogic-cloud/weblogic-kubernetes-operator!4975 (cherry picked from commit 28949ff733333b0b5dc1ba55fce241c562534944) c05fd8c4 Document certification of k8s 1.32 a5baff91 Update additional minimum versions --- .../introduction/prerequisites/introduction.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/documentation/site/content/introduction/prerequisites/introduction.md b/documentation/site/content/introduction/prerequisites/introduction.md index 6b8ed53aded..11763834426 100644 --- a/documentation/site/content/introduction/prerequisites/introduction.md +++ b/documentation/site/content/introduction/prerequisites/introduction.md @@ -7,12 +7,16 @@ weight: 5 For the current production release {{< latestVersion >}}: -* Kubernetes 1.24.0+, 1.25.0+, 1.26.2+, 1.27.2+, 1.28.2+, 1.29.1+, 1.30.1+, and 1.31.1+ (check with `kubectl version`). -* Flannel networking v0.13.0-amd64 or later (check with `docker images | grep flannel`), Calico networking v3.16.1 or later, +{{% notice note %}} +Support for Kubernetes 1.32.1+ with Oracle WebLogic Server 12.2.1.4 or 14.1.1.0 requires that the WebLogic container images have patch 37788099. No patch is required when using Oracle WebLogic Server 14.1.2.0. +{{% /notice %}} + +* Kubernetes 1.26.2+, 1.27.2+, 1.28.2+, 1.29.1+, 1.30.1+, 1.31.1+, 1.32.1+ (check with `kubectl version`). +* Flannel networking v0.26.7 or later (check with `docker images | grep flannel`), Calico networking v3.29.4 or later, *or* OpenShift SDN on OpenShift 4.3 systems. -* Docker 19.03.1+ (check with `docker version`) *or* CRI-O 1.20.2+ (check with `crictl version | grep RuntimeVersion`). -* Helm 3.3.4+ (check with `helm version --client --short`). -* For domain home source type `Model in Image`, WebLogic Deploy Tooling 1.9.11+. +* Docker 25.0.10+ (check with `docker version`) *or* CRI-O 1.26.2+ (check with `crictl version | grep RuntimeVersion`). +* Helm 3.13.2+ (check with `helm version --client --short`). +* For domain home source type `Model in Image`, WebLogic Deploy Tooling 4.3.4+. * Oracle WebLogic Server 12.2.1.4.0, Oracle WebLogic Server 14.1.1.0.0, or Oracle WebLogic Server 14.1.2.0.0. * **NOTE**: From d2b5536896300fad1ef102b7b3b592c392b55339 Mon Sep 17 00:00:00 2001 From: Ryan Eberhard Date: Tue, 27 May 2025 19:27:17 -0400 Subject: [PATCH 347/356] Dependency updates --- .../weblogic/kubernetes/ItConfigDistributionStrategy.java | 2 +- .../weblogic/kubernetes/ItKubernetesDomainEvents.java | 8 ++++---- .../weblogic/kubernetes/utils/CommonLBTestUtils.java | 4 ++-- .../kubernetes/operator/wlsconfig/MacroSubstitutor.java | 6 +++--- pom.xml | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java index 71424109701..9e092b0bb77 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItConfigDistributionStrategy.java @@ -1303,7 +1303,7 @@ private static String getHostAndPortOKE() { "Getting Nginx loadbalancer service node port failed"); hostAndPort = getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) != null - ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST + ":" + nginxNodePort; + ? getServiceExtIPAddrtOke(nginxServiceName, nginxNamespace) : K8S_NODEPORT_HOST + ":" + nginxNodePort; return hostAndPort; } } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java index 1a68015b434..edb8936e273 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItKubernetesDomainEvents.java @@ -265,10 +265,10 @@ void testDomainK8sEventsNonExistingManagedServer() { timestamp = now(); logger.info("patch the domain resource with non-existing managed server"); patchStr = "[{\"op\": \"add\",\"path\": \"" - + "/spec/managedServers/-\", \"value\": " - + "{\"serverName\" : \"nonexisting-ms\", " - + "\"serverStartPolicy\": \"IfNeeded\"}" - + "}]"; + + "/spec/managedServers/-\", \"value\": " + + "{\"serverName\" : \"nonexisting-ms\", " + + "\"serverStartPolicy\": \"IfNeeded\"}" + + "}]"; logger.info("Updating domain configuration using patch string: {0}\n", patchStr); patch = new V1Patch(patchStr); assertTrue(patchDomainCustomResource(domainUid, domainNamespace3, patch, V1Patch.PATCH_FORMAT_JSON_PATCH), diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java index 82030246c36..62d1e662358 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/CommonLBTestUtils.java @@ -786,12 +786,12 @@ public static void verifyClusterLoadbalancing(String domainUid, if (hostRouting) { curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' -v " + "-H 'host: %s' %s://%s/" + uri, ingressHostName, protocol, host) - : String.format("curl -g --show-error -ks --noproxy '*' " + : String.format("curl -g --show-error -ks --noproxy '*' " + "-H 'host: %s' %s://%s/" + uri, ingressHostName, protocol, getHostAndPort(host, lbPort)); } else { curlRequest = OKE_CLUSTER_PRIVATEIP ? String.format("curl -g --show-error -ks --noproxy '*' -v " + "%s://%s" + locationString + "/" + uri, protocol, host) - : String.format("curl -g --show-error -ks --noproxy '*' " + : String.format("curl -g --show-error -ks --noproxy '*' " + "%s://%s" + locationString + "/" + uri, protocol, getHostAndPort(host, lbPort)); } diff --git a/operator/src/main/java/oracle/kubernetes/operator/wlsconfig/MacroSubstitutor.java b/operator/src/main/java/oracle/kubernetes/operator/wlsconfig/MacroSubstitutor.java index 0c41dceab64..26b3123514d 100644 --- a/operator/src/main/java/oracle/kubernetes/operator/wlsconfig/MacroSubstitutor.java +++ b/operator/src/main/java/oracle/kubernetes/operator/wlsconfig/MacroSubstitutor.java @@ -1,4 +1,4 @@ -// Copyright (c) 2018, 2023, Oracle and/or its affiliates. +// Copyright (c) 2018, 2025, Oracle and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. package oracle.kubernetes.operator.wlsconfig; @@ -116,8 +116,8 @@ private String resolveMacroValue(String macro) { case "machineName" -> machineName; case "id" -> "" + id; default -> - // Look for macro in ConfigurationProperty or as system property - System.getProperty(macro); + // Look for macro in ConfigurationProperty or as system property + System.getProperty(macro); }; } } diff --git a/pom.xml b/pom.xml index 4b5922ec585..a354805e6af 100644 --- a/pom.xml +++ b/pom.xml @@ -708,7 +708,7 @@ 3.8.1 3.6.0 3.5.0 - 10.23.1 + 10.24.0 1.0 3.6.0 3.2.7 @@ -718,8 +718,8 @@ 2.0.1 1.0.39 1.9.0 - 1.5.6 - 1.4.0 + 1.5.7 + 1.5.0 1.18.0 1.7.3 0.1.0 From 90dc0a9d6b731f76f977cd049fa7e20e98edd4bd Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Wed, 4 Jun 2025 16:30:39 +0000 Subject: [PATCH 348/356] added debug --- .../ItServerStartPolicyConfigCluster.java | 11 ++++++++--- .../kubernetes/utils/ServerStartPolicyUtils.java | 13 +++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index ea9059db73e..f4acc917c27 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -124,9 +124,14 @@ public void beforeEach() { logger.info("Found standalone managed server configuration"); // Check configured cluster configuration is available - String configServerPodName = domainUid + "-config-cluster-server1"; - checkPodReadyAndServiceExists(configServerPodName, - domainUid, domainNamespace); + String configServerPodName = domainUid + "-config-cluster-server1"; domainUid, domainNamespace); + + try { + Thread.sleep(240000); + } catch (Exception ex) { + // + } + boolean isServerConfigured = checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); assertTrue(isServerConfigured, diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index b9d196e4b3e..5ca23d1f7eb 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -240,10 +240,19 @@ public static void createDomainResource( .serverPod(new ServerPod() .addEnvItem(new V1EnvVar() .name("JAVA_OPTIONS") - .value("-Dweblogic.StdoutDebugEnabled=false")) + .value("-Dweblogic.security.SSL.ignoreHostnameVerification=true -Xms1024m -Xmx1024m")) .addEnvItem(new V1EnvVar() .name("USER_MEM_ARGS") - .value("-Djava.security.egd=file:/dev/./urandom "))) + .value("-Djava.security.egd=file:/dev/./urandom ")) + .addEnvItem(new V1EnvVar() + .name("MEM_ARGS") + .value("-Xms1024m -Xmx1024m")) + .livenessProbe(new ProbeTuning() + .initialDelaySeconds(300) + .failureThreshold(3)) + .readinessProbe(new ProbeTuning() + .initialDelaySeconds(300) + .failureThreshold(3))) .adminServer(new AdminServer() .adminService(new AdminService() .addChannelsItem(new Channel() From fb6523e089bcb6f68e51b1d7ecf3f2814ca363c8 Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Wed, 4 Jun 2025 18:05:32 +0000 Subject: [PATCH 349/356] fixed an error --- .../kubernetes/ItServerStartPolicyConfigCluster.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index f4acc917c27..39d227a7dd1 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -124,14 +124,15 @@ public void beforeEach() { logger.info("Found standalone managed server configuration"); // Check configured cluster configuration is available - String configServerPodName = domainUid + "-config-cluster-server1"; domainUid, domainNamespace); + String configServerPodName = domainUid + "-config-cluster-server1"; + checkPodReadyAndServiceExists(configServerPodName, domainUid, domainNamespace); try { Thread.sleep(240000); } catch (Exception ex) { // } - + boolean isServerConfigured = checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); assertTrue(isServerConfigured, From 3edad6a927f3c74a2b8744e0504b7875ce9673cf Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Wed, 4 Jun 2025 19:32:23 +0000 Subject: [PATCH 350/356] added debug --- .../oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index 5ca23d1f7eb..098fd234834 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -24,6 +24,7 @@ import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ManagedServer; import oracle.weblogic.domain.Model; +import oracle.weblogic.domain.ProbeTuning; import oracle.weblogic.domain.ServerPod; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; From e9717f76466d2f1cd1c1b8348cee84f0b6553473 Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Thu, 5 Jun 2025 14:49:17 +0000 Subject: [PATCH 351/356] debugging --- .../kubernetes/ItServerStartPolicyConfigCluster.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 39d227a7dd1..9255fee7d62 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -128,11 +128,12 @@ public void beforeEach() { checkPodReadyAndServiceExists(configServerPodName, domainUid, domainNamespace); try { - Thread.sleep(240000); + Thread.sleep(120000); + //Thread.sleep(240000); } catch (Exception ex) { // } - + boolean isServerConfigured = checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); assertTrue(isServerConfigured, From 8aa0172a74b0ed5c7014727369c91884075dddc0 Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Mon, 9 Jun 2025 15:40:08 +0000 Subject: [PATCH 352/356] added another debug --- .../ItServerStartPolicyConfigCluster.java | 15 +++++++++++++-- .../kubernetes/utils/ServerStartPolicyUtils.java | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 9255fee7d62..1a117511b5b 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -23,6 +23,8 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.isPodRestarted; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkClusterReplicaCountMatches; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchServerStartPolicy; import static oracle.weblogic.kubernetes.utils.PodUtils.checkIsPodRestarted; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDeleted; @@ -42,6 +44,7 @@ import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.STOP_SERVER_SCRIPT; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.checkManagedServerConfiguration; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.executeLifecycleScript; +import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.isManagedServerConfiguration; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.managedServerNamePrefix; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.prepare; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.restoreEnv; @@ -127,17 +130,25 @@ public void beforeEach() { String configServerPodName = domainUid + "-config-cluster-server1"; checkPodReadyAndServiceExists(configServerPodName, domainUid, domainNamespace); + /* try { Thread.sleep(120000); //Thread.sleep(240000); } catch (Exception ex) { // - } + }*/ + + testUntil( + withLongRetryPolicy, + isManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName), + logger, + "Waiting until managed server from configured cluster found"); + /* boolean isServerConfigured = checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); assertTrue(isServerConfigured, - "Could not find managed server from configured cluster"); + "Could not find managed server from configured cluster");*/ logger.info("Found managed server from configured cluster"); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index 098fd234834..96bb209e923 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -296,6 +296,21 @@ public static void createDomainResource( + "for %s in namespace %s", domainUid, domNamespace)); } + /** + * Verify the server MBEAN configuration through rest API. + * @param managedServer name of the managed server + * @param domainNamespace domain namespace + * @param adminServerPodName name of the admin server + * @return true if MBEAN is found otherwise false + **/ + public static Callable isManagedServerConfiguration(String managedServer, + String domainNamespace, + String adminServerPodName) { + return () -> { + return checkManagedServerConfiguration(managedServer, domainNamespace, adminServerPodName); + }; + } + /** * Verify the server MBEAN configuration through rest API. * @param managedServer name of the managed server From ebc4bc26ffe2a15e7f1a990569c281809393ae80 Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Wed, 11 Jun 2025 13:52:44 +0000 Subject: [PATCH 353/356] removed testUntil it does not work --- .../ItServerStartPolicyConfigCluster.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 1a117511b5b..aaa39147609 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -23,8 +23,6 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.isPodRestarted; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkClusterReplicaCountMatches; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; -import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchServerStartPolicy; import static oracle.weblogic.kubernetes.utils.PodUtils.checkIsPodRestarted; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDeleted; @@ -44,7 +42,6 @@ import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.STOP_SERVER_SCRIPT; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.checkManagedServerConfiguration; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.executeLifecycleScript; -import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.isManagedServerConfiguration; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.managedServerNamePrefix; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.prepare; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.restoreEnv; @@ -130,25 +127,17 @@ public void beforeEach() { String configServerPodName = domainUid + "-config-cluster-server1"; checkPodReadyAndServiceExists(configServerPodName, domainUid, domainNamespace); - /* try { - Thread.sleep(120000); + Thread.sleep(180000); //Thread.sleep(240000); } catch (Exception ex) { // - }*/ - - testUntil( - withLongRetryPolicy, - isManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName), - logger, - "Waiting until managed server from configured cluster found"); + } - /* boolean isServerConfigured = checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); assertTrue(isServerConfigured, - "Could not find managed server from configured cluster");*/ + "Could not find managed server from configured cluster"); logger.info("Found managed server from configured cluster"); } From 47436ca9a8019477a57cad658a36aa57ad1954c6 Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Wed, 11 Jun 2025 17:16:15 +0000 Subject: [PATCH 354/356] final fix --- .../ItServerStartPolicyConfigCluster.java | 3 +-- .../kubernetes/utils/ServerStartPolicyUtils.java | 15 --------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index aaa39147609..33747bc3672 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -128,8 +128,7 @@ public void beforeEach() { checkPodReadyAndServiceExists(configServerPodName, domainUid, domainNamespace); try { - Thread.sleep(180000); - //Thread.sleep(240000); + Thread.sleep(240000); } catch (Exception ex) { // } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index 96bb209e923..098fd234834 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -296,21 +296,6 @@ public static void createDomainResource( + "for %s in namespace %s", domainUid, domNamespace)); } - /** - * Verify the server MBEAN configuration through rest API. - * @param managedServer name of the managed server - * @param domainNamespace domain namespace - * @param adminServerPodName name of the admin server - * @return true if MBEAN is found otherwise false - **/ - public static Callable isManagedServerConfiguration(String managedServer, - String domainNamespace, - String adminServerPodName) { - return () -> { - return checkManagedServerConfiguration(managedServer, domainNamespace, adminServerPodName); - }; - } - /** * Verify the server MBEAN configuration through rest API. * @param managedServer name of the managed server From 4086a4b1980359e61313453bf299b2558257b518 Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Thu, 12 Jun 2025 19:31:52 +0000 Subject: [PATCH 355/356] try testUtil again --- .../ItServerStartPolicyConfigCluster.java | 12 ++++++++++- .../utils/ServerStartPolicyUtils.java | 21 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java index 33747bc3672..926cc6a7232 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/ItServerStartPolicyConfigCluster.java @@ -23,6 +23,8 @@ import static oracle.weblogic.kubernetes.assertions.TestAssertions.isPodRestarted; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkClusterReplicaCountMatches; import static oracle.weblogic.kubernetes.utils.CommonTestUtils.checkPodReadyAndServiceExists; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.testUntil; +import static oracle.weblogic.kubernetes.utils.CommonTestUtils.withLongRetryPolicy; import static oracle.weblogic.kubernetes.utils.PatchDomainUtils.patchServerStartPolicy; import static oracle.weblogic.kubernetes.utils.PodUtils.checkIsPodRestarted; import static oracle.weblogic.kubernetes.utils.PodUtils.checkPodDeleted; @@ -42,6 +44,7 @@ import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.STOP_SERVER_SCRIPT; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.checkManagedServerConfiguration; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.executeLifecycleScript; +import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.isManagedServerConfiguration; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.managedServerNamePrefix; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.prepare; import static oracle.weblogic.kubernetes.utils.ServerStartPolicyUtils.restoreEnv; @@ -127,6 +130,13 @@ public void beforeEach() { String configServerPodName = domainUid + "-config-cluster-server1"; checkPodReadyAndServiceExists(configServerPodName, domainUid, domainNamespace); + testUntil( + withLongRetryPolicy, + isManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName), + logger, + "Waiting until managed server from configured cluster found"); + + /* try { Thread.sleep(240000); } catch (Exception ex) { @@ -136,7 +146,7 @@ public void beforeEach() { boolean isServerConfigured = checkManagedServerConfiguration("config-cluster-server1", domainNamespace, adminServerPodName); assertTrue(isServerConfigured, - "Could not find managed server from configured cluster"); + "Could not find managed server from configured cluster");*/ logger.info("Found managed server from configured cluster"); } diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index 098fd234834..3256ecab6db 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -248,11 +248,13 @@ public static void createDomainResource( .addEnvItem(new V1EnvVar() .name("MEM_ARGS") .value("-Xms1024m -Xmx1024m")) - .livenessProbe(new ProbeTuning() + .lilivenessProbevenessProbe(new ProbeTuning() .initialDelaySeconds(300) + .periodSeconds(60) .failureThreshold(3)) .readinessProbe(new ProbeTuning() .initialDelaySeconds(300) + .periodSeconds(60) .failureThreshold(3))) .adminServer(new AdminServer() .adminService(new AdminService() @@ -296,6 +298,21 @@ public static void createDomainResource( + "for %s in namespace %s", domainUid, domNamespace)); } + /** + * Verify the server MBEAN configuration through rest API. + * @param managedServer name of the managed server + * @param domainNamespace domain namespace + * @param adminServerPodName name of the admin server + * @return true if MBEAN is found otherwise false + **/ + public static Callable isManagedServerConfiguration(String managedServer, + String domainNamespace, + String adminServerPodName) { + return () -> { + return checkManagedServerConfiguration(managedServer, domainNamespace, adminServerPodName); + }; + } + /** * Verify the server MBEAN configuration through rest API. * @param managedServer name of the managed server @@ -312,7 +329,7 @@ public static boolean checkManagedServerConfiguration(String managedServer, .append(" http://" + adminServerPodName + ":7001") .append("/management/tenant-monitoring/servers/") .append(managedServer) - .append(" --silent --show-error ") + .append(" --silent --show-error --connect-timeout 300 --max-time 600 ") .append(" -o /dev/null") .append(" -w %{http_code}") .append(" && echo ${status}") From 56e3a237bacc3eec1c42c1126c863df3f1d5c6ab Mon Sep 17 00:00:00 2001 From: Huiling Zhao Date: Thu, 12 Jun 2025 19:39:32 +0000 Subject: [PATCH 356/356] minor change --- .../kubernetes/utils/ServerStartPolicyUtils.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java index 3256ecab6db..c85851fec3c 100644 --- a/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java +++ b/integration-tests/src/test/java/oracle/weblogic/kubernetes/utils/ServerStartPolicyUtils.java @@ -24,7 +24,7 @@ import oracle.weblogic.domain.DomainSpec; import oracle.weblogic.domain.ManagedServer; import oracle.weblogic.domain.Model; -import oracle.weblogic.domain.ProbeTuning; +//import oracle.weblogic.domain.ProbeTuning; import oracle.weblogic.domain.ServerPod; import oracle.weblogic.kubernetes.actions.impl.primitive.Command; import oracle.weblogic.kubernetes.actions.impl.primitive.CommandParams; @@ -219,7 +219,7 @@ public static void createDomainResource( String encryptionSecretName, String configmapName) { List securityList = new ArrayList<>(); - + // create the domain CR DomainResource domain = new DomainResource() .apiVersion(DOMAIN_API_VERSION) @@ -247,15 +247,7 @@ public static void createDomainResource( .value("-Djava.security.egd=file:/dev/./urandom ")) .addEnvItem(new V1EnvVar() .name("MEM_ARGS") - .value("-Xms1024m -Xmx1024m")) - .lilivenessProbevenessProbe(new ProbeTuning() - .initialDelaySeconds(300) - .periodSeconds(60) - .failureThreshold(3)) - .readinessProbe(new ProbeTuning() - .initialDelaySeconds(300) - .periodSeconds(60) - .failureThreshold(3))) + .value("-Xms1024m -Xmx1024m"))) .adminServer(new AdminServer() .adminService(new AdminService() .addChannelsItem(new Channel()